多线程_线程间通信

1.等待/通知机制

1.1 不使用等待/通知机制实现线程间通信

我们来设计一个场景,通过使用sleep()结合while(true)死循环来实现多个线程间通信
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
执行结果如下:如果是-server状态下,需要用volatile修饰list
在这里插入图片描述
虽然两个线程通过共享数据实现了通信,但是弊端在于,b线程需要不停的通过while条件轮询机制来检测list.size()的值,这样很消耗CPU资源

如果轮询的时间间隔小,更浪费CPU资源,如果轮询的时间间隔大,有可能取不到所要的数据.所以就需要一种机制来实现减少CPU的资源浪费,而且还能实现在多个线程间的通信,这就是wait/nptify机制

1.2 什么是等待/通知机制

在之前多线程之间也可以实现通信,原因是多个线程共同访问同一个变量,但那种通信机制不是"等待/通知",两个线程都是主动式的读取一个共享变量,在花费读取时间的基础上,读到的值是不是想要的,并不能完全确定

1.3 等待/通知机制的实现

wait():
作用:使当前执行代码的线程进行等待.
wait()是Object类的方法,该方法将当前线程置入"预执行队列",且在wait()所在的代码处停止执行,直到接到通知或中断为止.

在调用wait()方法时,线程必须先获取到该对象的对象级别锁,那么怎么获取到对象锁呢,也就是说在同步方法和同步块中调用wait()方法

notify():
作用:通过那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选一个呈wait状态的线程,对其发出notify()

总结:wait使线程停止运行,而notify使停止的线程继续运行
如果不在同步方法中,调用wait,就会抛出异常
在这里插入图片描述
当main线程,获取到该对象的对象锁后,执行wait
在这里插入图片描述
方法wait下面的代码不执行了

但线程不能永远等待下去,那样线程就停止不前,不继续向下进行了,如何使用wait状态的线程继续运行呢?答案就是使用notify()方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
执行结果如下:3秒后线程被notify通知唤醒了
在这里插入图片描述

使用wait()和notify()实现前面size()值等于5的实验
1.之前我们的做法,两个线程同时执行object对象的不同方法,A线程在mthod1方法中对对i++,而B线程在mthod2方法中,轮询i的值,当i=5时,抛出异常

而我们现在的做法是通过wait/notify来实现线程中的通信!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
创建一个Object lock = new Object()作为通信对象
t1线程执行,获取到lock对象的锁,开始执行同步代码块,此时判断MyList.size() != 5,所以进行了lock.wait();当前线程释放锁,同时进行等待
t2线程执行,获取到lock对象的锁(t1已经释放锁了),执行MyList.add(),当MyList.size() =5时,执行lock.notify方法,但是当前线程不会马上释放该对象锁,wait状态的线程也不会马上获取到该对象锁,只有等执行lock.notify的线程退出synchronized代码块后,当前线程才会释放锁
在这里插入图片描述
关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每一个Object都实现了wait()和notify()方法,他们必须被用在synchronized同步的Object的临界区内,通过调用wait()方法可以让处于临界区的线程进入等待状态,同时释放被同步对象的锁,而notify操作可以唤醒一个因调用wait操作而处于阻塞状态的线程,使其进入就绪状态.被重新唤醒的线程会试图重新获取临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码,如果发出notify操作时没有处于阻塞状态的线程,那么该命令会被忽略

wait()方法可以使调用该方法的线程释放同享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒
notify()方法可以随机唤醒等待队列中等待同一共享资源的一个线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知一个线程
notifyAll()方法可以使所有正在等待的队列中等待这一共享资源的全部线程从等待状态退出,进入可运行状态,此时优先级的线程优先执行

1.4 方法wait()锁释放与notify()锁不释放

执行wait()后,锁自动释放,而执行完notify()方法,锁不会自动释放

1.测试执行wait()后,锁自动释放
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果显示为:虽然使用了synchronized()同步代码块,但是并没有同步的效果,而如果改成sleep(),就成了同步了
在这里插入图片描述

2.测试执行notify()方法后,不释放锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
执行结果如下:
在这里插入图片描述
如上所示,必须执行完notify()方法所在的同步synchronized代码块后才释放锁

1.5 当interrupt方法遇到wait方法

当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常
在这里插入图片描述
在这里插入图片描述
显示结果如下:
在这里插入图片描述

总结:
1.执行完同步代码块后释放对象的锁
2.在执行同步代码块的过程中,遇到异常而导致线程终止,锁会释放
3.在执行同步代码块的过程中,执行了锁所属的对象的wait()方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒

1.6 只通知一个线程

在这里插入图片描述
3个自定义线程,每个线程都呈现wait状态
在这里插入图片描述
唤醒线程
在这里插入图片描述
结果如下:notify仅随机唤醒了一个线程
在这里插入图片描述
多次调用notify()方法时,会随机将等待wait状态的线程进行唤醒
在这里插入图片描述

1.7 唤醒所有线程

当notify方法的调用次数小于线程对象的数量,会出现部分线程对象无法被唤醒,为了唤醒所有的线程,可以使用notifyAll()方法
在这里插入图片描述

1.8 方法wait(long)的使用

1.9 通知过早

1.10 等待wait的条件发生变化

1.11 生产者/消费者模式实现

1.12 通过管道进行线程间通信:字节流

1.13 通过管道进行线程间通信:字符流

1.14 等待/通知之交叉备份

2.方法join的使用

3.类ThreadLocal的使用

4.类InheritableThreadLocal的使用

猜你喜欢

转载自blog.csdn.net/qq_24099547/article/details/90547532