一、进程与线程的概念
1.进程
进程的概念实在操作系统上体现的(电脑系统,手机),进程就是应用程序,例如:音乐,QQ,微信,王者荣耀,只要是应用程序并且打开了就是一个进程;执行中的程序就是进程
,此进程
是一个动态的概念–>进程中会有执行内容(运行状态,会有交互),并且赋予一定的独立功能;多进程就是在电脑的运行状态下可以打开多个应用程序开启多个进程,并且进程之间不会有相互干扰,并且可以同时运行,进程的运行要看的是CPU的核心数–>一个核心代表你可以开启一个进程(核心处理时什么样是最佳状态)–>CPU切换功能(当前在显示时主要显示的是当前打开的窗口)–>所以所有的进程都会抢占CPU的执行权–>所以软件程序开的越多越消耗CPU的资源
2.线程
线程的概念实在应用程序中提现的,并且应用程序(进程)想要执行就必须有一个线程(一般这个线程叫做主线程),线程的体现在应用程序运行
二、并发和并行的概念
1.并发
并发基本含义 在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行
2.并行
并行是指“并排行走”或“同时实行或实施”。 在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。 对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。
3.区别
- 并发:同时刻只能执行一条指令
- 并行:同时刻可以执行多条指令
三、Java中线程创建的几种方式
1.继承Thread
类
步骤:
- 创建一个类,继承Thread类
- 实现Thread类中run方法–>run方法是多线程的执行体(线程执行的逻辑代码)
- 创建测试类,并创继承了Thread类的类对象
- 调用start方法启动线程
创建自定义类继承Thread类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class MyThread extends Thread {
private String name;
public MyThread() { }
public MyThread(String name) { super(name); this.name = name; }
@Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(name + "线程执行:" + i); } } }
|
创建测试方法并创建线程启动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class ThreadDemo { public static void main(String[] args) { MyThread myThread1 = new MyThread("线程1"); myThread1.start(); MyThread myThread2 = new MyThread("线程2"); myThread2.start(); System.out.println("程序结束..."); } }
|
2.实现Runnable接口重写run方法
步骤:
- 创建线程类,并实现Runnable
- 重写run方法并编写逻辑代码
- 创建测试类并创建实现Runnable接口的类
- 创建Thread,并将线程类做参数传入
- 启动线程测试
线程类实现Runnable接口
1 2 3 4 5 6 7 8 9 10 11 12
| public class MyThread implements Runnable {
@Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("多线程执行:" + i); } } }
|
线程测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ThreadDemo { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.start(); thread2.start();
Thread thread3 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("匿名内部类多线程执行:" + i); } } }); thread3.start(); System.out.println("程序结束..."); } }
|
3.实现Callable接口创建线程
步骤:
- 创建线程类实现Callable接口,并实现call方法
- 通过FutureTask类创建线程–>传入Callable的实现类
- 创建Thread线程对象,并将FutureTask对象传入当做参数
- 启动线程
- 接收线程返回值
与前两不同,这里没有run方法,而是使用call方法去实现线程的逻辑代码,并且可以拥有返回值
,但是接收返回值时会造成线程的阻塞(暂停等待接收数据),同样不能通过同一个FutureTask对象创建两个线程创建方式
创建线程类实现Callable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class MyThread implements Callable<String> {
@Override public String call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println("线程执行:" + i); } return "线程执行完毕"; } }
|
创建测试类启动线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ThreadDemo { public static void main(String[] args) throws Exception { MyThread myThread = new MyThread(); FutureTask<String> futureTask1 = new FutureTask<>(myThread); FutureTask<String> futureTask2 = new FutureTask<>(myThread); Thread thread1 = new Thread(futureTask1); thread1.start(); Thread thread2 = new Thread(futureTask2); thread2.start(); String result1 = futureTask1.get(); System.out.println(result1); String result2 = futureTask2.get(); System.out.println(result2); System.out.println("程序结束..."); } }
|
4.三种方式优缺
继承Thread类
优点:继承后直接可以启动线程
缺点:Java是单继承的语言,一旦继承了则无法继承其他的类的,造成我们的可扩展性非常低,并且没有返回值
实现Runnable接口
优点:解决了单继承的扩展性低缺点
缺点:没有返回值,需要单独创建Thread线程类启动
实现Callable接口创建FutureTask对象
优点:带有返回值并且也是通过接口实现方式创建
缺点:使用麻烦,并且接收返回值会造成程序阻塞(暂停),并且一个FutureTask对象只能启动一个线程
四、Java中线程的生命周期以及状态控制
1.线程的生命周期概述
新建状态(New)
使用new关键字创建了Thread线程类之后并且在调用start()方法之前就是新建状态,一旦调用start()方法则进入就绪状态
就绪状态(Runnable)
调用start()方法后并且具备所有的可执行状态(没有错误的情况下)就是就绪状态–>一旦进入就绪状态后则能够去争夺CPU的执行权,仅仅只是争夺并没有执行权,所有的线程都需要去争夺线程执行权之后才能进入到运行状态
运行状态(Running)
当处于就绪状态的线程争夺到CPU的执行权后即可进入并且执行run方法的逻辑(执行run方法就是执行状态)这时才是运行状态
如果在运行状态时丢失CPU的执行权则进入就绪状态,如果调用sleep/join/等待同步锁/执行IO操作/wait方法时是进入阻塞状态
如果调用yield方法则也会重新进入就绪状态
阻塞状态(Blocked)
当运行状态的run方法中执行sleep/join/等待同步锁/执行IO操作/wait方法时则会出现阻塞状态(相当于执行途中暂停),这种状态时同样也是丢失CPU的执行权
- 等待阻塞:运行中调用wait方法则是代表进入等待阻塞状态,只有等其他线程调用
notify
或notifyAll
方法时才会被唤醒重新进入就绪状态
- 同步等待:当获取执行等待锁时会进入同步等待–>当前某个对象有其他线程正在执行,只能等待其他线程执行完毕后才允许获取,获取同步锁后才会重新进入到就绪状态
- 其他状态:当线程代用join/IO操作时,join线程让步代表当前这次进入阻塞状态让其他先行,方法调用完毕后则重新进入就绪状态;IO操作由于是针对文件的操作,会循环读取或者读写时会造成程序阻塞(在读写数据时有一个过程,这个过程就是造成程序阻塞原因)
消亡状态(Dead)
线程run方法执行完毕后就是线程的消亡
2.线程的休眠
public static native void sleep(long millis) throws InterruptedException
:线程休眠millis是代表毫秒值
public static void sleep(long millis, int nanos) throws InterruptedException
:线程休眠millis是毫秒值,nanos纳秒值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class MyThread extends Thread {
public MyThread(String name) { super(name); }
@Override public void run() { for (int i = 0; i < 10; i++) { try { if (i == 5) { Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getName() + "-线程执行:" + i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class A线程休眠 { public static void main(String[] args) throws InterruptedException { MyThread myThread1 = new MyThread("线程休眠1"); MyThread myThread2 = new MyThread("线程休眠2"); myThread1.start(); myThread2.start();
System.out.println("主线程休眠开始..."); Thread.sleep(5000); System.out.println("主线程休眠结束..."); } }
|
3.线程的优先级
每个线程执行时都有一个优先级,设置优先级可以让CPU优先选择,但是CPU选不选择不光是看优先级的–>虽然可以设置但是最终的执行结果不一定是优先级最高的
public final void setPriority(int newPriority)
:设置线程优先级别优先级数值为1-10,默认是5
Thread类静态常量;以下三个静态常量是Thread类提供设置线程优先级时使用
- 最低优先级 public final static int MIN_PRIORITY = 1;
- 中等优先级(默认) public final static int NORM_PRIORITY = 5;
- 最高优先级 public final static int MAX_PRIORITY = 10;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class MyThread implements Runnable {
private int priority;
public MyThread() {
}
public MyThread(int priority) { this.priority = priority; }
@Override public void run() { Thread thread = Thread.currentThread(); thread.setPriority(this.priority); for (int i = 0; i < 10; i++) { System.out.println(thread.getName() + "-线程执行:" + i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Demo { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread1 = new Thread(myThread, "线程1"); Thread thread2 = new Thread(myThread, "线程2"); Thread thread3 = new Thread(myThread, "线程3"); thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.NORM_PRIORITY); thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); } }
|
4.线程让步
线程让步是当前正在执行的线程处于孔融让梨精神放弃此次执行的机会,让给其他线程执行,但是让归让不代表我不进行争夺,有可能会出现我让步了我有不小心争夺到了执行权;线程让步会让当前线程重新进入就绪状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Demo { public static void main(String[] args) { MyThread01 myThread1 = new MyThread01(); MyThread02 myThread2 = new MyThread02(); Thread thread1 = new Thread(myThread1, "让步线程"); Thread thread2 = new Thread(myThread2, "不让步线程"); thread1.start(); thread2.start(); Thread.yield(); System.out.println("main方法中线程让步"); } }
|
5.线程合并(线程加入插入)
当前有两个线程,线程A和线程B,比如执行线程时先让B执行,并且线程B调用join插入其中,这时线程A就必须等待线程B的执行结束;相当于线程A和线程B执行时,线程A充了钱,线程会在线程B直线执行,并且线程B会等待线程A的执行完毕
public final void join() throws InterruptedException
:线程合并(或者叫插入)
public final synchronized void join(long millis) throws InterruptedException
:线程合并(或者叫插入),插入多少毫秒然后离开
public final synchronized void join(long millis, int nanos) throws InterruptedException
:线程合并(或者叫插入),插入多少毫秒/纳秒然后离开
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MyThread extends Thread {
public MyThread(String name) { super(name); }
@Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "-线程执行:" + i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Demo { public static void main(String[] args) { MyThread myThread01 = new MyThread("线程1"); myThread01.start(); try { myThread01.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println("主线程执行:" + i); } } }
|
6.守护线程
守护线程存在意义是为了守护程序中的线程,并且守护线程不会主动死亡(只要执行了并且给了一个死循环则不会主动结束),只有当所有线程(非守护线程外的)全部消亡后才会跟随死亡
守护线程可以理解是后台线程–>在后台默默的工作的线程,无私奉献的
如果想让普通线程切换为守护线程只需要调用方法setDaemon
并且必须是在调用start
方法前调用
public final void setDaemon(boolean on)
:将当前线程设置为守护线程
守护线程类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MyThread extends Thread { @Override public void run() { int index = 0; while (true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("守护线程执行:" + (index++)); } } }
|
普通线程类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MyThread01 extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程执行:" + i); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Demo { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); myThread.setDaemon(true); myThread.start();
MyThread01 myThread01 = new MyThread01(); myThread01.start();
Thread.sleep(2000);
for (int i = 0; i < 100; i++) { System.out.println("main方法执行:" + i); } } }
|
五、线程同步synchronized
1.synchronized关键字注意事项
- synchronized只能修饰方法和代码块,不能修饰变量和类
- synchronized只有在多线程读取同一个共享数据时才需要使用,如果多线程不操作共享资源(同一个资源)则不需要使用synchronized关键字修饰
- 经过synchronized关键字修饰后不管是方法还是代码执行效率都会降低,并且还会出现死锁问题
- 同步方法的同步监视器是
this
对象,静态同步方法的同步监视器是类名.class
类,同步代码块的同步监视器是自定义的(一般锁住的是共享资源)
- 同步方法和静态同步方法是将整个方法体锁住,甭管里面是否是所有的代码都需要锁住;同步代码块是只需要锁住共享资源即可能稍微的提高效率
2.synchronized关键字时是什么时候获取锁什么时候释放锁
- 同步方法和静态同步方法是在调用方法时大括号的起始位开始锁,大括号的结束位置是释放锁–>方法执行完毕才会释放同步锁
- 同步代码块是在同步代码块执行时开始锁,执行结束时释放同步锁
- 如果发生异常或者使用return/break关键字时也会提前结束
- 如果同步方法或同步代码块调用了wait()方法也会释放锁对象
3.synchronized关键字的可重入性
在使用一个同步方法时,是否可以调用另一个同步方法,就是俄罗斯套娃
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ThreadSynchronizedDemo { public static void main(String[] args) { func01(); }
public static synchronized void func01() { System.out.println("静态同步方法1"); func02(); }
public static synchronized void func02() { System.out.println("静态同步方法2"); func03(); }
public static void func03() { System.out.println("普通静态方法3"); } }
|
六、线程同步的死锁
1.概述
线程死锁可能会出现的场景:假设有一个共享类,共享类中有两个属性,属性1和属性2,并且有两个线程,线程A和线程B,线程A操作共享类中的属性1和属性2,同样线程B也须要操作属性1和属性2,但是我们要求属性1和属性2他们两个都必须是安全的(数据不能出现问题),将他俩全部锁住线程1中锁住属性1并且在锁住属性1的同步代码块中锁属性2,相同的线程B再锁住属性2,在属性2的同步代码块中锁属性1,这样就可以实现死锁现象,原因是,当线程A拿到属性1的锁后会进入,在线程A拿到属性1的锁时线程B有可能已经拿到属性2的锁,拿到属性2的锁的时候同样会去拿属性1的锁,而这时属性1的锁在线程A手中,会等待,同样线程A想要拿属性2的锁时,属性2同样在线程B的手中,这样就造成了死锁问题
2. 代码演示死锁问题
共享资源类
1 2 3 4 5 6 7 8
| /** * 共享资源类 */ public class Source { // 两个共享属性 public static Object object01 = new Object(); public static Object object02 = new Object(); }
|
线程A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| /** * 实现死锁的线程A */ public class ThreadA extends Thread {
/** * 实现死锁 */ @Override public void run() { // 锁住属性1-->Object01 synchronized (Source.object01) { System.out.println("ThreadA拿到object01的锁..."); // 休眠10毫秒,目的是给线程B留下拿属性2锁的机会 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 取得属性2的锁 synchronized (Source.object02) { System.out.println("ThreadA拿到object02的锁..."); } } } }
|
线程B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
public class ThreadB extends Thread {
@Override public void run() { synchronized (Source.object02) { System.out.println("ThreadB拿到object02的锁..."); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Source.object01) { System.out.println("ThreadB拿到object01的锁..."); } } } }
|
测试类
1 2 3 4 5 6 7 8 9 10
| public class Demo { public static void main(String[] args) { ThreadA threadA = new ThreadA(); ThreadB threadB = new ThreadB(); threadA.start(); threadB.start(); } }
|
七、线程通讯(wait/notify/notifyAll)
1.生产者与消费者模式
生产者–>代表是商户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class Producer { private String mealName;
public String getMealName() { return mealName; }
public void setMealName(String mealName) { this.mealName = mealName; }
@Override public String toString() { return "Producer{" + "mealName='" + mealName + '\'' + '}'; } }
|
缓冲区–>外卖桌子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
public class Buffer {
private Producer producer;
private boolean flag;
public Buffer(Producer producer) { this.producer = producer; }
public synchronized void producer(String mealName) { if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.producer.setMealName(mealName); System.out.println("---------生产者,商户做好了:" + producer.getMealName()); flag = true; this.notify(); }
public synchronized void consumer() { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者,外卖小哥取走了:" + this.producer.getMealName()); flag = false; this.notify(); } }
|
生产者线程类–>生产商品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public class ProducerThread implements Runnable {
private Buffer buffer;
public ProducerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { for (int i = 0; ; i++) { if (i % 2 == 0) { buffer.producer("酱焖肘子"); continue; } if (i % 3 == 0) { buffer.producer("铁锅炖大ne"); continue; } if (i % 7 == 0) { buffer.producer("猪肉炖粉条"); continue; } } } }
|
消费者线程类–>外卖小哥取外卖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ConsumerThread implements Runnable {
private Buffer buffer;
public ConsumerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { while (true) { buffer.consumer(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo { public static void main(String[] args) { Producer producer = new Producer(); Buffer buffer = new Buffer(producer); ProducerThread producerThread = new ProducerThread(buffer); ConsumerThread consumerThread = new ConsumerThread(buffer); new Thread(producerThread).start(); new Thread(consumerThread).start();
} }
|
2.生产者与消费者存在问题
上面在使用生产者与消费者时是单线程,正常交替,如果说出现两条线程同时生产并且同时消费,那这时会不会出现问题????
一旦出现了多生产者和多消费者后就会造成会有一方取走|制作多次
以上问题解决方案,将原本if改为while循环判断是否存在商品或外卖
解决多取|多做问题
生产者–>代表是商户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class Producer { private String mealName;
public String getMealName() { return mealName; }
public void setMealName(String mealName) { this.mealName = mealName; }
@Override public String toString() { return "Producer{" + "mealName='" + mealName + '\'' + '}'; } }
|
缓冲区–>外卖桌子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
public class Buffer {
private Producer producer;
private boolean flag;
public Buffer(Producer producer) { this.producer = producer; }
public synchronized void producer(String mealName) { while (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.producer.setMealName(mealName); System.out.println("---------生产者,商户做好了:" + producer.getMealName()); flag = true; this.notify(); }
public synchronized void consumer() { while (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者,外卖小哥取走了:" + this.producer.getMealName()); flag = false; this.notify(); } }
|
生产者线程类–>生产商品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public class ProducerThread implements Runnable {
private Buffer buffer;
public ProducerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { for (int i = 0; ; i++) { if (i % 2 == 0) { buffer.producer("酱焖肘子"); continue; } if (i % 3 == 0) { buffer.producer("铁锅炖大ne"); continue; } if (i % 7 == 0) { buffer.producer("猪肉炖粉条"); continue; } } } }
|
消费者线程类–>外卖小哥取外卖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ConsumerThread implements Runnable {
private Buffer buffer;
public ConsumerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { while (true) { buffer.consumer(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo { public static void main(String[] args) { Producer producer = new Producer(); Buffer buffer = new Buffer(producer); ProducerThread producerThread = new ProducerThread(buffer); ConsumerThread consumerThread = new ConsumerThread(buffer); new Thread(producerThread).start(); new Thread(consumerThread).start();
} }
|
以上代码解决了多做|多取的问题了,但是使用的是notify方法,在唤醒时有可能唤醒不是对方而是己方,一旦唤醒是己方则会出现死锁(都在wait等待唤醒)
解决多做|多取的问题并且解决死等(死锁)问题
生产者–>代表是商户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class Producer { private String mealName;
public String getMealName() { return mealName; }
public void setMealName(String mealName) { this.mealName = mealName; }
@Override public String toString() { return "Producer{" + "mealName='" + mealName + '\'' + '}'; } }
|
缓冲区–>外卖桌子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
public class Buffer {
private Producer producer;
private boolean flag;
public Buffer(Producer producer) { this.producer = producer; }
public synchronized void producer(String mealName) { while (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.producer.setMealName(mealName); System.out.println("---------生产者,商户做好了:" + producer.getMealName()); flag = true; this.notifyAll(); }
public synchronized void consumer() { while (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者,外卖小哥取走了:" + this.producer.getMealName()); flag = false; this.notifyAll(); } }
|
生产者线程类–>生产商品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public class ProducerThread implements Runnable {
private Buffer buffer;
public ProducerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { for (int i = 0; ; i++) { if (i % 2 == 0) { buffer.producer("酱焖肘子"); continue; } if (i % 3 == 0) { buffer.producer("铁锅炖大ne"); continue; } if (i % 7 == 0) { buffer.producer("猪肉炖粉条"); continue; } } } }
|
消费者线程类–>外卖小哥取外卖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ConsumerThread implements Runnable {
private Buffer buffer;
public ConsumerThread(Buffer buffer) { this.buffer = buffer; }
@Override public void run() { while (true) { buffer.consumer(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo { public static void main(String[] args) { Producer producer = new Producer(); Buffer buffer = new Buffer(producer); ProducerThread producerThread = new ProducerThread(buffer); ConsumerThread consumerThread = new ConsumerThread(buffer); new Thread(producerThread).start(); new Thread(consumerThread).start();
} }
|
虽然以上方法可以解决线程死锁问题,但是会出现资源浪费的情况,原因是唤醒时没必要将所有的线程全都唤醒,唤醒时只需要唤醒对方即可
八、拓展阅读
深入浅出Java多线程
Java线程
https://github.com/i-xiaoxin/2022/10/11/Java线程/