Java多线程之 - wait,notify,notifyall作用、用法
程序员文章站
2022-05-04 20:50:06
...
- 阻塞阶段
执行wait 要获得这个对象的monitor 锁 ,调用wait后线程就处于阻塞状态了,直到以下四种之一情况发生,才会被唤醒.- 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程;
- 另一个线程调用这个对象的notifyAll()方法;
- 过了wait(long timeout)规定的超时时间,如果传入0就是永久等待;
- 线程自身调用了interrupt()
- 唤醒阶段
notify唤醒单个正在等待某对象monitor的线程,有多个线程在等待会随机唤醒一个线程nofy得在synchronized保护块内部运行,如若在外部会抛出异常。如果获得锁的线程调用了notifyall则会唤醒所有等待线程然后随机有个线程将会获得锁。 - 遇到中断
抛出异常,释放当前获得的monitor。
模拟 当线程wait 之后 其他线程使用 notify 唤醒线程。
public class Wait{
public static Object object = new Object();
static class Thread1 extends Thread{
@Override
public void run(){
synchronized (object){
System.out.println("线程1开始执行了");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1" +Thread.currentThread().getName() +"获取到了锁");
}
}
}
static class Thread2 extends Thread{
@Override
public void run(){
synchronized (object){
object.notify();
System.out.println("线程2" +Thread.currentThread().getName() +"调用了notify()");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1= new Thread1();
Thread2 thread2= new Thread2();
thread1.start();
Thread.sleep(200);
thread2.start();
}
}
西面的代码模仿 线程 A 和 线程B 先后获取锁 然后wait()释放锁 然后线程C再去唤醒线程A和线程B 继续执行 .
public class WaitNotifyAll implements Runnable {
private static Object resourceA = new Object();
public static void main(String[] args) throws InterruptedException {
Runnable r = new WaitNotifyAll();
Thread threadA = new Thread(r);
Thread threadB = new Thread(r);
Thread threadC = new Thread(new Runnable() {
public void run() {
synchronized (resourceA){
resourceA.notifyAll();
System.out.println("Thread notified");
}
}
});
threadA.start();
threadB.start();
Thread.sleep(1000);
threadC.start();
}
public void run() {
synchronized (resourceA){
System.out.println(Thread.currentThread().getName() + "got resourceA lock");
try {
System.out.println(Thread.currentThread().getName() + "waits to start");
resourceA.wait();
System.out.println(Thread.currentThread().getName() + "is waiting to end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
有A锁和B锁 先是获取了A锁 然后获取了B锁 然后释放了A锁 然后第二个线程可以拿到A锁 但是B锁并没有被释放
public class WaitNotifyReleaseOwnMonitor {
private static volatile Object resourceA = new Object();
private static volatile Object resourceB = new Object();
public static void main(String[] args) {
Thread threada = new Thread(new Runnable() {
public void run() {
synchronized (resourceA){
System.out.println("Thread A got ResourceA lock");
synchronized (resourceB){
System.out.println("ThreadA got resourceB lock");
try{
resourceA.wait();
System.out.println("ThreadA releases resourceA lock.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread threadb = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA){
System.out.println("ThreadB got resourceA lock");
synchronized (resourceB){
System.out.println("ThreadB got resourceB lock");
}
}
}
});
threada.start();
threadb.start();
}
}
wait,notify,notifyall特点、性质
- 用前必须拥有monitor
- notify只能唤醒其中一个由取决于JVM
- 属于Object类
- 类似功能的Condition
- 同时持有多个锁的情况
手写生产者消费者设计模式
- 为什么使用生产者和消费者模式
在线程中 有的线程是生产出数据 有的是消费数据 但两者之间速度是不一致的 如果有一个设计模式能解决他们之间相互等待的问题 而不至于一个过快一个过慢,相互配合比较困难 将消费方 和生产方 之间解耦 以便于更加好的配合。
生产者 把生产出来的数据放入 中间的阻塞队列 消费者再通过阻塞队列去取 这样一来就有个缓冲区这样就是把他们的能力进行平衡 ,当生产者生产数据过多时阻塞消费者消化,等到差不多是再提醒生产者继续生产 。
消费者 生产者模式代码 实现 2个线程 一个生产者 一个消费者 轮流获得锁 去生产和消费轮流进行。
public class ProducerConsumerModel {
public static void main(String[] args) {
EventStorage eventStorage =new EventStorage();
Producer producer = new Producer(eventStorage);
Consumer consumer = new Consumer(eventStorage);
Thread threada = new Thread(producer);
Thread threadb =new Thread(consumer);
threada.start();
threadb.start();
}
}
class Consumer implements Runnable {
private EventStorage<Integer> storge;
public Consumer(EventStorage storage){
this.storge = storage;
}
public void run() {
for(int i=0;i<100;i++){
storge.take();
}
}
}
class Producer implements Runnable {
private EventStorage<Integer> storge;
public Producer(EventStorage storage){
this.storge = storage;
}
public void run() {
for(int i=0;i<100;i++){
storge.put(i);
}
}
}
class EventStorage<E>{
private int maxSize;
private LinkedList<E> storage;
public EventStorage(){
maxSize = 10;
storage = new LinkedList<E>();
}
public synchronized void put(E e){
while(storage.size() ==maxSize){
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
storage.add(e);
System.out.println("仓库里有了" +storage.size() +"个产品.");
notify();
}
public synchronized void take() {
while (storage.size() == 0) {
try {
//notify(); 如果写在这 就是当消费光了再去生产
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("仓库拿到了" + storage.poll() + "剩余:"+storage.size());
//每次消费了就去生产
notify();
}
}
wait,notify,notifyAll常见问题
两个线程交替打印 0~100 的奇偶性
public class WaitNotifyPrint0ddEvenSyn implements Runnable {
int i=0;
public static void main(String[] args) throws InterruptedException {
WaitNotifyPrint0ddEvenSyn waitNotifyPrint0ddEvenSyn = new WaitNotifyPrint0ddEvenSyn();
Thread thread1 = new Thread(waitNotifyPrint0ddEvenSyn,"偶数线程");
Thread thread2 = new Thread(waitNotifyPrint0ddEvenSyn,"奇数线程");
thread1.start();
Thread.sleep(1000);
thread2.start();
}
public void run() {
synchronized (this){
while(i<100){
if((i & 1) == 0){
System.out.println( Thread.currentThread().getName()+"打印奇数" + i );
i++;
notify();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println( Thread.currentThread().getName()+"打印偶数" + i);
i++;
notify();
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
notifyAll();
}
}
}
- 为什么wait()需要在同步代码块内使用,而sleep不需要
- 通信变得可靠,防止死锁发生 假设设计是先要执行wait()之后执行notify()唤醒 但如果wait()没有同步代码块保护 随时可能被其他线程切换过去执行notify 等其他线程执行完 notify 线程 再执行wait 这样这个线程就永远会等待下去了. 每人去唤醒它了.slee()是针对单独线程的不影响其他线程所以不需要放到同步代码块中。
- 为什么线程通信的方法wait(),notify()和notifyall()被定义在Object类里?而sleep定义在Thread类里?
- wait(),notify()和notifyall() 是锁级别的操作 锁属于某个对象的 每一个对象的对象头都有过几个字段来保存锁的状态的 所以锁是和对象绑定的 而不是和 某个线程绑定。如果把所定义在线程里 那么逻辑不够灵活很多情况下 需要1个线程 可以拥有多把锁配合 就像1个人可以拥有好几把锁 配合开不同的门。
- wait方法是属于Object对象的 ,那调用Thread.wait 会怎么样?
- Thread 也是一个继承与Object的对象,但Thread 会自动的调用notify流程会受到干扰,故不要用Thread作为锁。
- 如何选择用notify ()还是 notifyAll ()?
- 考虑到现实情况 需要唤醒一个 还是多个
- notifyAll 之后所有的线程都会再次抢夺锁,如果其他线程抢夺失败怎么办?
- 没抢到的会回到最初始的状态,等待抢到锁。
- 用
suspend()和resume()来阻塞线程可以吗?为什么?- 已经不推荐了,改用notyify() 和 wait()
下一篇: 风趣搞笑的校园生活
推荐阅读
-
java多线程之wait(),notify(),notifyAll()的详解分析
-
java多线程之wait(),notify(),notifyAll()的详解分析
-
Java多线程中wait、notify、notifyAll使用详解
-
Java多线程的wait(),notify(),notifyAll()、sleep()和yield()方法使用详解
-
java多线程同步代码块wait(),notify()和notifyAll()
-
java并发之线程间协作的两种方式:wait、notify、notifyAll和Condition
-
Java多线程之synchronized | notify | notifyAll | wait | 两个线程交替运行
-
java多线程之wait/notify/notifyAll
-
Java多线程之 - wait,notify,notifyall作用、用法
-
(五)Java线程之间的通信之wait notify 及Condition用法await Signal