0
点赞
收藏
分享

微信扫一扫

Java多线程基础知识总结

楚木巽 2022-04-17 阅读 73
java

目录

前言

进程与线程的关系

同步与异步

并发与并行

多线程实现方式(理论及代码!!!)

Thread类----常用方法(重点)

线程的生命周期

线程同步

线程不安全问题

线程安全-同步代码块(隐式锁)

线程安全-同步方法(隐式锁)

线程安全-显示锁Lock

线程死锁

多线程通信问题---生产者与消费者问题

线程的六种状态

带返回值的线程Callable

线程池Executors

缓存线程池

定长线程池

单线程线程池

周期性任务定长线程池

Lambda表达式

总结


前言

通过几天的学习,我对于Java多线程知识有了一定的掌握,下面是我在学习过程中总结的笔记,与各位分享一波,新手小白初来乍到,望各位不吝赐教。


进程与线程的关系

进程:是指一个内存中运行的应用程序,每个进程都有一个独立内存空间

线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。

线程被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程调度Java虚拟机负责线程的调度。多线程的意义:为了让多条执行路径能够均分CPU,能够更合理的交替执行

分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度Java):优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个。

同步与异步

同步:排队执行,效率低但是安全
异步:同步执行,效率高但是数据不安全

并发与并行

并发:指两个或多个事件在同一个时间段内发生
并行:指两个或多个事件在同一时刻发生(同时发生)

多线程实现方式(理论及代码!!!)

继承Thread,实现Runnable

package com.javaMultithreading;

/**
 * 多线程技术
 * 实现Runnable 与 继承Thread相比 有如下的优势:
 * 1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
 * 2.可以避免单继承所带来的局限性
 * 3.任务与线程本身是分离的,提高了程序的健壮性
 * 4.后续学习的线程池技术,接收Runnable类型的任务,不接受Thread类型的线程
 */
public class Demo1 {
    public static void main(String[] args) {
        //继承Thread
        //MyThread thread = new MyThread();
        //thread.start();
        //for (int i=0;i<10;i++){
        //    System.out.println("汗滴禾下土"+i);
        //}

        //实现Runnable
        //1.创建一个任务对象
        MyRunnable r = new MyRunnable();
        //2.创建一个线程,并未其分配一个任务
        Thread t = new Thread(r);
        //3.执行这个线程
        t.start();
        for (int i=0;i<10;i++){
            System.out.println("疑是地上霜"+i);
        }
    }
}

package com.javaMultithreading;

public class MyThread extends Thread{
    /**
     * run方法就是线程要执行的任务方法
     */
    @Override
    public void run() {
        //这里的代码,就是一条新的执行路径
        //这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start()来启动任务
       for (int i=0;i<10;i++){
           System.out.println("锄禾日当午"+i);
       }
    }
}

package com.javaMultithreading;
/**
 * 用于给线程执行的任务
*/
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //线程任务
        for (int i=0;i<10;i++){
            System.out.println("床前明月光"+i);
        }
    }
}

Thread匿名内部类,Runnable匿名内部类

package com.javaMultithreading;

/**
 * 多线程技术
 * Thread匿名内部类实现多线程
 * Runnable匿名内部类实现多线程
 */
public class Demo2 {
    public static void main(String[] args) {
        //Thread匿名内部类实现多线程
        //new Thread(){
        //    @Override
        //    public void run() {
        //        for (int i=0;i<10;i++){
        //            System.out.println("一二三四五"+i);
        //        }
        //    }
       // }.start();
        
        //Runnable匿名内部类实现多线程
        new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i=0;i<10;i++){
                    System.out.println("一二三四五"+i);
                }
            }
        }).start();

        for (int i=0;i<10;i++){
            System.out.println("六七八九十"+i);
        }

    }
}

Thread类----常用方法(重点)

Thread类 --常用方法
构造方法---
Thread(Runnable target)
Thread(Runnable target,String name)
Thread(String name)


Demo3:设置和获取线程名称
package com.javaMultithreading;

public class Demo3 {
    public static void main(String[] args) {
        //如何获取线程的名称
        //currentThread()----返回对当前正在执行的线程对象的引用。
        System.out.println(Thread.currentThread().getName());//正在执行的线程的名称
        new Thread(new MyRunnable(),"锄禾日当午").start();
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());;
        }
    }
}

Demo4:线程休眠sleep---阻塞状态
package com.javaMultithreading;

public class Dmeo4 {
    public static void main(String[] args) throws InterruptedException {
        //线程的休眠 sleep(long millis)----导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数
        for(int i=0;i<10;i++){
            System.out.println(i);
            Thread.sleep(1000);//每隔一秒输出一次
        }
    }
}

Demo5:线程的中断
/**如果该线程阻塞的调用wait() , wait(long) ,或wait(long, int)的方法Object类,
或的join() , join(long) , join(long, int) , sleep(long) ,
或sleep(long, int) ,这个类的方法,那么它的中断状态将被清除,
并且将收到InterruptedException 。 */


package com.javaMultithreading;

public class Demo5 {
    public static void main(String[] args) {
        //线程的中断
        //一个线程是一个独立执行路径,它是否应该结束,应该由其自身决定
       Thread t1 = new Thread(new MyRunnable());
       t1.start();

        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        t1.interrupt();//给线程t1添加中断标记
    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    //System.out.println("发现了中断标记,但是我们就是不死亡");
                    System.out.println("发现了中断标记,我们这个线程自杀");
                    return;
                }
            }
        }
    }
}

Demo6:守护线程
package com.javaMultithreading;

public class Demo6 {
    public static void main(String[] args) {
        //线程:分为守护线程和用户线程
        //用户线程:当一个进程不包含任何的存活的用户线程时,进程结束
        //守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡
       Thread t1 = new Thread(new MyRunnable());
       //设置t1为守护线程
       t1.setDaemon(true);
       t1.start();
        for (int i=1;i<=5;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Demo20线程优先级
package com.javaMultithreading;

public class Demo20 {
    /**
     * 线程优先级
     * setPriority​(int newPriority)--更改此线程的优先级。 
     * 注意:一个线程被启动时,他的优先级与启动他的线程一致
     * @param args
     */
    public static void main(String[] args) {
        MyThread t1 = new MyThread("高优先级");
        //t1.setPriority(Thread.MAX_PRIORITY);
        t1.setPriority(10);
        MyThread t2 = new MyThread("中优先级");
        t2.setPriority(5);
        MyThread t3 = new MyThread("低优先级");
        t3.setPriority(1);
        t1.start();
        t2.start();
        t3.start();
    }

    static class MyThread extends Thread{
        public MyThread(String name){
            setName(name);
        }

        @Override
        public void run() {
            for (int i=0;i<10;i++){
                System.out.println(getName()+":"+i);
            }
        }
    }
}

线程的生命周期

创建:当程序使用new关键字创建了一个线程之后,该线程就处于一个新建状态。

就绪:当线程对象调用了Thread.start()方法之后,该线程处于就绪状态。

运行:添加到了线程列表中,如果OS调度选中了,就会进入到运行状态。

阻塞:线程因为某种原因放弃CPU使用权,暂时停止运行。

  1.           等待阻塞----执行wai()方,JVM会把该线程放入等待池中。
  2.          同步阻塞----运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
  3.          其他阻塞----运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。

死亡----线程会以以下三种方式之一结束,结束后就处于死亡状态:

  • run()方法执行完成,线程正常结束
  • 线程抛出一个未捕获的Exception或Error
  • 直接调用该线程的stop()方法来结束该线程

线程同步

线程不安全问题

package com.javaMultithreading;

public class Demo7 {
    public static void main(String[] args) {
        //线程不安全
        //多线程同时操作一个资源,容易发生问题
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        @Override
        public void run() {
            while (count>0){
                //卖票
                System.out.println("正在准备卖票");
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("出票成功,余票:"+count);
            }
        }
    }
}

线程安全-同步代码块(隐式锁)

package com.javaMultithreading;

/**
 * 线程同步:synchronized
 */
public class Demo8 {
    public static void main(String[] args) {
        //线程不安全
        //解决方案1.同步代码块
        //格式:  synchronized(锁对象){
        //
        //       }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        static Object o = new Object();//固定一把锁
        
        @Override
        public void run() {
            while (true){
                synchronized (o){
                    if (count>0){
                        //卖票
                        System.out.println("正在准备卖票");
                        System.out.println();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
                    }else {
                        break;
                    }
                }
            }
        }
    }
}

线程安全-同步方法(隐式锁)

package com.javaMultithreading;

public class Demo9 {
    public static void main(String[] args) {
        //线程不安全
        //解决方案2.同步方法
        //格式:  synchronized(锁对象){
        //
        //       }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        private Object o = new Object();

        @Override
        public void run() {
            while (true){
                boolean flag = sale();
                if (!flag){
                    break;
                }
            }
        }

        public synchronized boolean sale(){
            if (count>0) {
                //卖票
                System.out.println("正在准备卖票");
                System.out.println();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                return true;
            }
            return false;
        }
    }


}

线程安全-显示锁Lock

公平锁与非公平锁

公平锁:先来先到

非公平锁:抢占

package com.javaMultithreading;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo10 {
    public static void main(String[] args) {
        //线程不安全
        //解决方案3.显示锁 Lock 子类ReentrantLock
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable {
        //票数
        private int count = 10;
        //显示锁 l:fair参数为true 就表示是公平锁
        private Lock l = new ReentrantLock(true);

        @Override
        public void run() {
            while (true) {
                l.lock();
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    System.out.println();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                } else {
                    break;
                }
                l.unlock();
            }
        }
    }


}

线程死锁

package com.javaMultithreading;

public class Demo11 {
    public static void main(String[] args) {
        //线程死锁
        Culprit c = new Culprit();
        Police p = new Police();
        new MyThread(c,p).start();
        c.say(p);
    }

    static class MyThread extends Thread{
        private Culprit c;
        private Police p;
        public MyThread(Culprit c,Police p){
            this.c = c;
            this.p = p;
        }

        @Override
        public void run() {
            p.say(c);
        }
    }

    //罪犯
    static class Culprit{
        public synchronized void say(Police p){
            System.out.println("罪犯:你放了我,我放了人质");
            p.fun();
        }
        public synchronized void fun(){
            System.out.println("罪犯被放走了,罪犯也放了人质");
        }
    }

    //警察
    static  class Police{
        public synchronized void say(Culprit c){
            System.out.println("警察:你放了人质,我放过你");
            c.fun();
        }
        public synchronized void fun(){
            System.out.println("警察救了人质,但是罪犯跑了");
        }
    }
}

多线程通信问题---生产者与消费者问题

package com.javaMultithreading;

public class Demo12 {
    /**
     *多线程通信问题,生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f){
            this.f = f;
        }

        @Override
        public void run() {
            for (int i=0;i<100;i++){
                if (i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        //true表示可以生产
        private boolean flag = true;
        public synchronized void setNameAndSaste(String name,String taste) {
           if (flag){
               this.name = name;
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               this.taste = taste;
               flag = false;
               this.notifyAll();//唤醒服务员
               try {
                   this.wait();//自己睡眠
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        }
        public synchronized void get(){
            if (!flag){
                System.out.println("服务员端走的菜的名称是:"+name+",味道:"+taste);
                flag = true;
                this.notifyAll();//唤醒厨师
                try {
                    this.wait();//自己睡眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

线程的六种状态

NEW----尚未启动的线程处于此状态

RUNNABLE----在Java虚拟机中执行的线程处于此状态

BLOCKED----被阻塞灯代监视器锁定的线程处于此状态

WAITING----无限期等待另一个线程执行特定操作的线程处于此状态

TIMED_WAITING----正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态

TERMINATED----已退出的线程处于此状态

带返回值的线程Callable

Runnable与Callable的相同点

  1. 都是接口
  2. 都可以编写多线程程序
  3. 都采用Thread.start()启动线程

Runnable与Callable的不同点

  1. Runnable没有返回值;Callable可以返回执行结果
  2. Callable接口的call()允许抛出异常;Runnable的run()不能抛出
package com.javaMultithreading;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo13 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> c = new MyCallable();
        //FutureTask可用于包装Callable或Runnable对象。
        // 因为FutureTask实现Runnable , FutureTask可以将FutureTask提交给Executor以供执行
        FutureTask<Integer> task = new FutureTask<>(c);//通过FutureTask.get()得到返回值
        new Thread(task).start();
        Integer j = task.get();
        System.out.println("线程返回值为:"+j);
        for (int i=0;i<10;i++){
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

    static class MyCallable implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            //Thread.sleep(3000);
            for (int i=0;i<10;i++){
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            return 100;
        }
    }
}

线程池Executors

线程池的好处

  1. 降低资源消耗
  2. 提高响应速度
  3. 提高线程的可管理性

缓存线程池

package com.javaMultithreading;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo15 {
    /**
     * 缓存线程池
     * 长度无限制
     * 执行流程
     * 判断线程池是否存在空闲线程
     * 存在则使用
     * 不存在,则创建线程,并放入线程池,然后使用
     * @param args
     */
    public static void main(String[] args) {
        //向线程池中加入新的任务
        ExecutorService service = Executors.newCachedThreadPool();
        //指挥线程池执行新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"汗滴禾下土");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"谁知盘中餐");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"粒粒皆辛苦");
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"悯农");
            }
        });
    }
}

定长线程池

package com.javaMultithreading;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo17 {
    /**
     * 定长线程池
     * 长度是指定的数值
     * 任务加入后的执行流程:
     * 判断线程池是否存在空闲线程
     * 存在则使用
     * 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
     * 不存在空闲线程,且线程池已满的情况下,则灯代线程池存在空闲线程
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }
}

单线程线程池

package com.javaMultithreading;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo18 {
    /**
     * 单线程线程池
     * 执行流程
     * 判断线程池的那个线程是否空闲
     * 空闲则使用
     * 不空闲则等待,池中的单个线程空闲后使用
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });

    }
}

周期性任务定长线程池

package com.javaMultithreading;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo16 {
    /**
     * 周期任务 定长线程池
     * 执行流程
     * 判断线程池是否存在空闲线程
     * 存在则使用
     * 不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
     * 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
     *
     * 周期性任务执行时:
     * 定时执行,当某个时机触发时,自动执行某任务
     */
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 1.定长执行一次
         * 参数1.定时执行的任务
         * 参数2.时长数字
         * 参数3.时长数字的时间单位,TimeUnit的常量指定
         */
        /*service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        },5, TimeUnit.SECONDS);*/
        /**
         * 周期性执行任务
         * 参数1:任务
         * 参数2:延迟时长数字(第一次执行在什么时间以后)
         * 参数3:周期时长数字(每隔多久执行一次)
         * 参数4:时长数字的单位
         */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("hhhhhhhhh");
            }
        },5,1,TimeUnit.SECONDS);
    }
}

Lambda表达式

package com.javaMultithreading;

public class Demo19 {
    /**
     * Lambda表达式
     * 函数式编程思想
     * 面向对象:创建对象调用方法 解决问题
     *
     */
    public static void main(String[] args) {
        //冗余的Runnable代码
       /* Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        });
        t.start();*/
        /*Thread t = new Thread(() -> {
            System.out.println("锄禾日当午");
        });
        t.start();*/

        print((int x,int y)->{
                return x+y;
        },200,300);

    }

    public static void print(MyMath m,int x,int y){
        int num = m.sum(x,y);
        System.out.println(num);
    }

    static interface MyMath{
        int sum(int x,int y);
    }
}

举报

相关推荐

0 条评论