最后更新:2020年10月29日11点13分
一、线程的状态1、NEW(未启动)至今尚未启动的线程处于这种状态。
2、RUNNABLE(运行状态)正在 Java 虚拟机中执行的线程处于这种状态。
3、BLOCKED(锁租塞)受阻塞并等待某个监视器锁的线程处于这种状态。
4、WAITING(无线等待状态)无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
5、TIMED_WAITING(计时等待)等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
6、TERMINATED已退出的线程处于这种状态。
7、图解(这三张图相互补充吧)
二、等待唤醒案例分析WAITING(无线等待状态):三、等待唤醒案例代码实现1、等待唤醒案例(线程之间的通信):创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法放弃cpu执行权,进入WAITTING状态;
创建一个老板线程(生产者):花一段时间做包子,做好之后调用notify方法,唤醒顾客吃包子;
2、注意:①顾客和老板必须使用同步带马夸爱包裹起来,保证等待和唤醒只能有一个在运行;
②同步使用的锁对象(任意对象)必须唯一;
③只有锁对象才能调用wait和notify方法;
3、Object类中的方法:①void wait()在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待;
②void notify()唤醒在此对象监视器上等待的单个线程;
4、代码演示代码语言:javascript复制package study.thread;
public class ThreadWaitAndNotify {
public static void main(String[] args) {
//创建一个锁对象
Object object = new Object();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
super.run();
//使用同步技术,保证等待和唤醒只能有一个在运行
synchronized (object){
System.out.println("顾客向老板陈述所需包子的种类和数量");
//顾客进入等待状态
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//顾客结束等待状态,进入运行状态,吃包子
System.out.println("顾客吃包子");
}
}
}.start();
//创建一个老板线程
new Thread(){
@Override
public void run() {
super.run();
synchronized (object){
System.out.println("老板做好了包子");
object.notify();
}
}
}.start();
}
}四、Object类中的带参数的wait方法和notifyAll方法1、线程进入到TIMED_WAITING计时等待状态的两种方法:①使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到RUNNABLE(运行状态)或者BLOCKED(锁租塞)状态;
②使用wait(long m)方法,在毫秒值结束之后,如果线程没有被notify唤醒,则会自动醒来进入到RUNNABLE(运行状态)或者BLOCKED(锁租塞)状态;
2、线程唤醒的两个方法①void notify()唤醒在此对象监视器上等待的单个线程;
②void notifyAll()唤醒在此对象监视器上等待的所有线程;
五、线程间通信1、概念多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同;
2、为什么要处理线程间的通信多个线程并发执行时,在默认其情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一个任务时,那么多线程之间需要通过通信进行协调,以此来帮助我们达到多线程共同操作同一份数据的目的;
3、如何保证线程间通信有效利用资源为了避免多个线程同时操作同一个变量,有效利用资源,需要用到线程通信,即线程的等待唤醒机制;
六、等待唤醒机制概述1、wait方法:线程不再允许,进入无限等待状态,等到其他方法使用了notify方法唤醒,指定wait之后的代码;
若此时有多个线程在等待唤醒,那就唤醒最先等待的那个;
2、notify方法:唤醒一个正在等待的线程,若此时有多个线程在等待唤醒,那就唤醒最先等待的那个;
3、notifyAll方法:唤醒所有正在等待的线程;
4、备注:当线程被唤醒并不意味着线程立即进入运行状态,还要等待CPU的分配;
七、唤醒等待机制需求分析八、唤醒等待机制代码实现包子类:代码语言:javascript复制package study.thread;
public class Bun {
boolean flag = false;
}顾客类:代码语言:javascript复制package study.thread;
public class Client implements Runnable {
private Bun bun;
public Client(Bun bun) {
this.bun = bun;
}
@Override
public void run() {
synchronized (bun){
while (true) {
if (bun.flag) {
System.out.println("有包子,顾客吃包子");
break;
} else {
try {
System.out.println("没包子,顾客进入等待状态");
bun.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}老板类:代码语言:javascript复制package study.thread;
public class Boss implements Runnable {
private final Bun bun;
public Boss(Bun bun) {
this.bun = bun;
}
@Override
public void run() {
synchronized (bun){
if(!bun.flag){
System.out.println("老板做包子");
bun.flag = true;
bun.notify();
}
}
}
}测试类:代码语言:javascript复制package study.thread;
public class TestClientAndBoss {
public static void main(String[] args) {
Bun object = new Bun();
Client client = new Client(object);
Boss boss = new Boss(object);
new Thread(client).start();
new Thread(boss).start();
}
}九、知识点补充1、如何做到主线程等待所有子线程执行完毕之后再继续执行的?方法一(主线程睡眠法-sleep法):代码语言:javascript复制public class Test {
public static void main(String[] args) throws InterruptedException {
//1、主线程开始执行
System.out.println("1、主线程开始执行……");
//2、开启子线程
new Thread(() -> System.out.println("2、子线程1开始执行……")).start();
new Thread(() -> System.out.println("2、子线程2开始执行……")).start();
//3、主线程等待1秒钟
Thread.sleep(1000);
//4、主线程等待所有子线程执行完毕,继续执行后续代码
System.out.println("主线程等待所有子线程执行完毕,继续执行后续代码……");
}
}方法一概述:
方法一通过使主线程睡眠的方式等待所有子线程执行完完毕,所睡眠的时间是主观设置的,若子线程的任务较为复杂,则未必能准确把握睡眠时间,所以此方法不推荐使用。
方法二(使用Thread的join()):概述:
等待该线程终止,意思就是等待指定线程执行完毕之后再继续执行;
代码演示:
代码语言:javascript复制public class Test {
public static void main(String[] args) throws InterruptedException {
//1、主线程开始执行
System.out.println("1、主线程开始执行……");
//2、开启子线程
Runnable runnable1 = () -> System.out.println("2、子线程1开始执行……");
Runnable runnable2 = () -> System.out.println("2、子线程2开始执行……");
Thread thread1 =new Thread(runnable1);
Thread thread2 =new Thread(runnable2);
thread1.start();
thread2.start();
//3、主线程等待所有子线程执行完毕
thread1.join();
thread2.join();
//4、继续执行后续代码
System.out.println("主线程等待所有子线程执行完毕,继续执行后续代码……");
}
}方法三(使用CountDownLatch类):CountDownLatch类简介:
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待;
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
意思就是:使用一个线程计数器,每一个线程执行完毕之后减去1,在线程执行完毕之前await方法一致会让线程等待;
代码示例:
代码语言:javascript复制public class Test {
public static void main(String[] args) throws InterruptedException {
//1、主线程开始执行
System.out.println("1、主线程开始执行……");
//初始化一个线程数量数量为2的计数器
CountDownLatch count = new CountDownLatch(2);
//2、开启子线程
Runnable runnable1 = () -> {
System.out.println("2、子线程1开始执行……");
//子线程1执行完毕之后,计数器减去1
count.countDown();
};
Runnable runnable2 = () -> {
System.out.println("2、子线程2开始执行……");
//子线程2执行完毕之后,计数器减去1
count.countDown();
};
Thread thread1 =new Thread(runnable1);
Thread thread2 =new Thread(runnable2);
thread1.start();
thread2.start();
//3、当计数器为0的时候,停止等待
count.await();
System.out.println("3、计数器为0,结束等待");
//4、主线程等待所有子线程执行完毕,继续执行后续代码
System.out.println("4、主线程等待所有子线程执行完毕,继续执行后续代码……");
}
}方法四(使用CyclicBarrier类):CyclicBarrier类简介:
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
说明:
这个方法没有想象中那么简单,这个方法的意思是:所有的子线程先把事情做到同一个阶段(执行到屏障后等待其他线程也执行到屏障),然后执行(CyclicBarrier 支持一个可选的 Runnable 命令 ),最后执行屏障点之后的内容。
我在这里使用很妥协的方法实现了(主线程等待所有子线程执行完毕之后再继续执行的),但我觉得这个类的主要目的肯定不是为了做这种事情的!
代码演示:
代码语言:javascript复制package study.thread.diy;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
//1、主线程开始执行
System.out.println("1、主线程开始执行……");
//初始化一个线程数量数量为2的计数器
CyclicBarrier barrier = new CyclicBarrier(5,() -> {
System.out.println("4、所有线程通过屏障后执行……");
goOn();
});
for (int i = 0; i <5 ; i++) {
int j = i;
new Thread(() -> {
System.out.println("2、子线程"+ j +"执行……");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
// //子线程开始执行完毕之后,会执行4,要达到执行完所有子线程后继续执行的目的,我觉得可以这样
// //5、所有的子线程执行完毕,继续执行后续代码
// System.out.println("5、所有的子线程执行完毕,继续执行后续代码……");
}
private static void goOn(){
System.out.println("继续执行的代码");
}
}运行结果:
代码语言:javascript复制1、主线程开始执行……
2、子线程0执行……
2、子线程2执行……
2、子线程4执行……
2、子线程1执行……
2、子线程3执行……
4、所有线程通过屏障后执行……
继续执行的代码备注:这个类远远没有这么简单,使用的时候需要细细研究。
2、线程方法(2020年10月28日09点15分)
3、线程停止方法:不推荐使用JDK提供是stop()、destroy()方法(已废弃);
推荐线程自己停下来(最安全的);
建议使用一个标志位进行终止变量(当flag=false时,停止线程);
代码演示:代码语言:javascript复制package com.zb.thread;
//测试停止线程
//建议使用一个标志位进行终止变量(当flag=false时,停止线程);
public class TestStop implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("子线程" + i++);
}
}
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程" + i);
if(i==900){
testStop.stop();
System.out.println("子线程停止!");
}
}
}
}运行结果:4、线程礼让含义:线程礼让指的是使得当前正在执行的线程进入就绪状态,供CPU重新选择,但是礼让不一定成功,因为CPU调用谁是随机的,即使礼让了,也可能立即被调用;
代码演示:代码语言:javascript复制package com.zb.thread;
//测试线程礼让
public class TestYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行!");
System.out.println(Thread.currentThread().getName() + "线程开始礼让!");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName() + "线程结束执行!");
}
public static void main(String[] args) {
new Thread(new TestYield(),"a").start();
new Thread(new TestYield(),"b").start();
}
}运行结果:5、线程插队执行简介:join合并线程,待此线程执行完毕之后,再执行其他线程(此时其他线程进入阻塞状态),也就是插队执行;
代码演示:代码语言:javascript复制package com.zb.thread;
//测试线程插队
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("子线程" + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
System.out.println("主线程" + i);
if(i==300){
System.out.println("子线程开始插队!");
thread.join();
}
}
}
}运行结果:6、线程状态观测代码演示:代码语言:javascript复制package com.zb.thread;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//new
Thread.State state = thread.getState();
System.out.println(state);
//start
thread.start();
System.out.println(thread.getState());
//阻塞(睡眠)
Thread.sleep(100);//让主线程睡眠,让子线程执行睡眠
//只要线程未停止,就一直输出其状态
System.out.println(thread.getState());
while (state != Thread.State.TERMINATED){
System.out.println(thread.getState());
Thread.sleep(100);
state = thread.getState();
}
System.out.println(thread.getState());
}
}运行结果:代码语言:javascript复制NEW 新建状态
RUNNABLE 运行状态
TIMED_WAITING 等待状态
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
RUNNABLE 运行状态
TERMINATED 停止状态7、线程优先级简介:线程的优先级高不一定先执行,还是要看CPU调度,但是优先级高意味着权重高,被调度的可能性高;
代码演示:代码语言:javascript复制package com.zb.thread;
//设置优先级
public class TestPriority {
public static void main(String[] args) {
//主线程的优先级
System.out.println(Thread.currentThread().getName() + "==>" + Thread.currentThread().getPriority());
//创建几个线程,设置优先级
Thread t1 = new Thread(new MyPriority());
Thread t2 = new Thread(new MyPriority());
Thread t3 = new Thread(new MyPriority());
Thread t4 = new Thread(new MyPriority());
Thread t5 = new Thread(new MyPriority());
//要在start之前设置优先级
t1.setPriority(1);
t1.start();
t2.setPriority(3);
t2.start();
t3.setPriority(5);
t3.start();
t4.setPriority(7);
t4.start();
t5.setPriority(9);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "==>" + Thread.currentThread().getPriority());
}
}运行结果:8、守护(daemon)线程概述:线程分为用户线程和守护线程;
虚拟机必须确保用户线程执行完毕;
虚拟机不用等待守护线程执行完毕;
守护线程如:后台记录操作日志、监控内存、垃圾回收等;
代码演示:代码语言:javascript复制package com.zb.thread;
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//将上帝设置为守护线程
thread.start();
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝守护着你!");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("你活着!");
}
System.out.println("你走了!");
}
}运行结果:(上帝线程未执行完毕,但是上帝线程是守护线程,虚拟机不用等待守护线程执行完毕,所以也停止了!)
十、管程法——解决生产与消费的问题1、概述生产者:负责生产数据的模块(可能是方法、对象、线程、进程);
消费者:负责处理数据的模块(可能是方法、对象、线程、进程);
缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”;
基本逻辑:生产者将生产的数据放入缓冲区,消费者从缓冲区拿出数据;
图示:2、代码实现代码语言:javascript复制package com.zb.thread;
//测试生产者消费者模型,利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
private final SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产第" + i + "只鸡!");
}
}
}
//消费者
class Consumer extends Thread{
private final SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了第" + container.pop().id + "只鸡!");
}
}
}
//产品
class Chicken{
int id;//编号
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者生产产品
public synchronized void push(Chicken chicken){
//如果容器满了,就要等待消费者消费
if(count==chickens.length){
//容器满了,通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//这个时候等待消费者消费完,生产者继续放入鸡
//如果容器没有满,就需要丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if(count==0){
//没有鸡,等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//生产者进行了唤醒,可以消费了
//如果可以消费
count--;
Chicken chicken = chickens[count];
//消费鸡之后,通知生产者生产
this.notifyAll();
return chicken;
}
}3、运行结果代码语言:javascript复制生产第0只鸡!
生产第1只鸡!
生产第2只鸡!
生产第3只鸡!
生产第4只鸡!
生产第5只鸡!
生产第6只鸡!
生产第7只鸡!
生产第8只鸡!
生产第9只鸡!
消费了第9只鸡!
生产第10只鸡!
消费了第10只鸡!
生产第11只鸡!
消费了第11只鸡!
生产第12只鸡!
消费了第12只鸡!
生产第13只鸡!
消费了第13只鸡!
生产第14只鸡!
消费了第14只鸡!
生产第15只鸡!
生产第16只鸡!
消费了第15只鸡!
消费了第16只鸡!
生产第17只鸡!
消费了第17只鸡!
生产第18只鸡!
消费了第18只鸡!
生产第19只鸡!
消费了第19只鸡!
消费了第20只鸡!
消费了第8只鸡!
消费了第7只鸡!
消费了第6只鸡!
消费了第5只鸡!
消费了第4只鸡!
消费了第3只鸡!
消费了第2只鸡!
消费了第1只鸡!
消费了第0只鸡!
生产第20只鸡!
生产第21只鸡!
生产第22只鸡!
生产第23只鸡!
生产第24只鸡!
生产第25只鸡!
生产第26只鸡!
生产第27只鸡!
生产第28只鸡!
生产第29只鸡!
生产第30只鸡!
生产第31只鸡!
消费了第21只鸡!
消费了第31只鸡!
消费了第32只鸡!
消费了第30只鸡!
消费了第29只鸡!
消费了第28只鸡!
消费了第27只鸡!
消费了第26只鸡!
消费了第25只鸡!
消费了第24只鸡!
生产第32只鸡!
消费了第23只鸡!
生产第33只鸡!
消费了第33只鸡!
消费了第34只鸡!
消费了第22只鸡!
生产第34只鸡!
生产第35只鸡!
消费了第35只鸡!
消费了第36只鸡!
生产第36只鸡!
生产第37只鸡!
消费了第37只鸡!
消费了第38只鸡!
生产第38只鸡!
生产第39只鸡!
消费了第39只鸡!
消费了第40只鸡!
生产第40只鸡!
生产第41只鸡!
消费了第41只鸡!
消费了第42只鸡!
生产第42只鸡!
生产第43只鸡!
消费了第43只鸡!
消费了第44只鸡!
生产第44只鸡!
生产第45只鸡!
消费了第45只鸡!
生产第46只鸡!
消费了第46只鸡!
生产第47只鸡!
消费了第47只鸡!
生产第48只鸡!
消费了第48只鸡!
生产第49只鸡!
消费了第49只鸡!
生产第50只鸡!
消费了第50只鸡!
生产第51只鸡!
消费了第51只鸡!
生产第52只鸡!
消费了第52只鸡!
生产第53只鸡!
消费了第53只鸡!
生产第54只鸡!
消费了第54只鸡!
生产第55只鸡!
消费了第55只鸡!
生产第56只鸡!
消费了第56只鸡!
生产第57只鸡!
消费了第57只鸡!
消费了第58只鸡!
生产第58只鸡!
生产第59只鸡!
消费了第59只鸡!
生产第60只鸡!
消费了第60只鸡!
生产第61只鸡!
消费了第61只鸡!
生产第62只鸡!
消费了第62只鸡!
生产第63只鸡!
消费了第63只鸡!
生产第64只鸡!
消费了第64只鸡!
生产第65只鸡!
消费了第65只鸡!
消费了第66只鸡!
生产第66只鸡!
生产第67只鸡!
消费了第67只鸡!
生产第68只鸡!
消费了第68只鸡!
生产第69只鸡!
消费了第69只鸡!
生产第70只鸡!
消费了第70只鸡!
生产第71只鸡!
生产第72只鸡!
生产第73只鸡!
生产第74只鸡!
生产第75只鸡!
生产第76只鸡!
生产第77只鸡!
生产第78只鸡!
生产第79只鸡!
生产第80只鸡!
生产第81只鸡!
消费了第71只鸡!
消费了第81只鸡!
消费了第82只鸡!
消费了第80只鸡!
消费了第79只鸡!
消费了第78只鸡!
消费了第77只鸡!
消费了第76只鸡!
消费了第75只鸡!
消费了第74只鸡!
消费了第73只鸡!
消费了第72只鸡!
生产第82只鸡!
生产第83只鸡!
消费了第83只鸡!
消费了第84只鸡!
生产第84只鸡!
生产第85只鸡!
消费了第85只鸡!
消费了第86只鸡!
生产第86只鸡!
生产第87只鸡!
生产第88只鸡!
生产第89只鸡!
生产第90只鸡!
生产第91只鸡!
生产第92只鸡!
生产第93只鸡!
生产第94只鸡!
生产第95只鸡!
生产第96只鸡!
生产第97只鸡!
消费了第87只鸡!
消费了第97只鸡!
生产第98只鸡!
生产第99只鸡!
消费了第98只鸡!
消费了第99只鸡!
消费了第96只鸡!
消费了第95只鸡!
消费了第94只鸡!
消费了第93只鸡!
消费了第92只鸡!
消费了第91只鸡!
消费了第90只鸡!
消费了第89只鸡!
消费了第88只鸡!十一、信号灯法——解决生产与消费的问题1、代码实现代码语言:javascript复制package com.zb.thread;
//测试生产者消费者,标志位:信号灯法
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者——演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
this.tv.play("《快乐大本营》");
}else {
this.tv.play("《抖音:记录美好生活!》");
}
}
}
}
//消费者——观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品——节目
class TV{
//演员表演的时候观众等待
//观众观看,演员等待
String voice;//表演的节目
boolean flag = true;
//表演
public synchronized void play(String voice){
if(!flag){//如果有节目就等待观众观看完毕
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有节目或者观众观看完毕之后进行表演
System.out.println("演员表演了" + voice);
//表演结束就通知观众观看通知观众观看
this.voice = voice;
this.flag = !this.flag;
this.notifyAll();
}
//观看
public synchronized void watch(){
//如果没有节目就等待演员表演
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有节目,或者等待被唤醒就去观看
System.out.println("观众观看了" + voice);
//观看之后,通知演员表演进行表演
this.flag = !this.flag;
this.notifyAll();
}
}2、运行结果代码语言:javascript复制演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》
演员表演了《快乐大本营》
观众观看了《快乐大本营》
演员表演了《抖音:记录美好生活!》
观众观看了《抖音:记录美好生活!》