博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程学习(4)wait/notify
阅读量:7305 次
发布时间:2019-06-30

本文共 5083 字,大约阅读时间需要 16 分钟。

hot3.png

\本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法。

①wait() 与 notify/notifyAll 方法必须在同步代码块中使用

②wait() 与  notify/notifyAll() 的执行过程

③中断 调用wait()方法进入等待队列的 线程

④notify 通知的顺序不能错

⑤多线程中测试某个条件的变化用 if 还是用 while?

①wait() 与 notify/notifyAll 方法必须在同步代码块中使用

wait() 与 notify/notifyAll() 是Object类的方法,在执行两个方法时,要先获得锁。

我们经常使用synchronized关键字获得锁。因此,wait()与notify/notifyAll()经常与synchronized搭配使用,即在synchronized修饰的同步代码块或方法里面调用wait() 与  notify/notifyAll()方法。

不在synchronized关键字中使用,会抛出异常。

②wait() 与  notify/notifyAll() 的执行过程

由于 wait() 与  notify/notifyAll() 是放在同步代码块中的,因此线程在执行它们时,肯定是进入了临界区中的,即该线程肯定是获得了锁的。

当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。

 当执行notify/notifyAll方法时,会唤醒一个处于等待该 对象锁 的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。

从这里可以看出,notify/notifyAll()执行后,并不立即释放锁,而是要等到执行完临界区中代码后,再释放。故,在实际编程中,我们应该尽量在线程调用notify/notifyAll()后,立即退出临界区。即不要在notify/notifyAll()后面再写一些耗时的代码。

示例如下:

1 public class Service { 2  3     public void testMethod(Object lock) { 4         try { 5             synchronized (lock) { 6                 System.out.println("begin wait() ThreadName=" 7                         + Thread.currentThread().getName()); 8                 lock.wait(); 9                 System.out.println("  end wait() ThreadName="10                         + Thread.currentThread().getName());11             }12         } catch (InterruptedException e) {13             e.printStackTrace();14         }15     }16 17     public void synNotifyMethod(Object lock) {18         try {19             synchronized (lock) {20                 System.out.println("begin notify() ThreadName="21                         + Thread.currentThread().getName() + " time="22                         + System.currentTimeMillis());23                 lock.notify();24                 Thread.sleep(5000);25                 System.out.println("  end notify() ThreadName="26                         + Thread.currentThread().getName() + " time="27                         + System.currentTimeMillis());28             }29         } catch (InterruptedException e) {30             e.printStackTrace();31         }32     }33 }

在第3行的testMethod()中调用 wait(),在第17行的synNotifyMethod()中调用notify()

从上面的代码可以看出,wait() 与  notify/notifyAll()都是放在同步代码块中才能够执行的。如果在执行wait() 与  notify/notifyAll() 之前没有获得相应的对象锁,就会抛出:java.lang.IllegalMonitorStateException异常。

在第8行,当ThreadA线程执行lock.wait();这条语句时,释放获得的对象锁lock,并放弃CPU,进入等待队列。

当另一个线程执行第23行lock.notify();,会唤醒ThreadA,但是此时它并不立即释放锁,接下来它睡眠了5秒钟(sleep()是不释放锁的,事实上sleep()也可以不在同步代码块中调用),直到第28行,退出synchronized修饰的临界区时,才会把锁释放。这时,ThreadA就有机会获得另一个线程释放的锁,并从等待的地方起(第9行)起开始执行。

接下来是两个线程类,线程类ThreadA调用testMethod()方法执行lock.wait();时被挂起,另一个线程类ThreadB调用synNotifyMethod()负责唤醒挂起的线程。代码如下:

public class ThreadA extends Thread {	private Object lock;	public ThreadA(Object lock) {		super();		this.lock = lock;	}	@Override	public void run() {		Service service = new Service();		service.testMethod(lock);	}}public class ThreadB extends Thread {	private Object lock;	public ThreadB(Object lock) {		super();		this.lock = lock;	}	@Override	public void run() {		Service service = new Service();		service.synNotifyMethod(lock);	}}public class Test {	public static void main(String[] args) {		Object lock = new Object();		ThreadA a = new ThreadA(lock);		ThreadB b = new ThreadB(lock);		a.start();		b.start();	}}输出结果:begin wait() ThreadName=Thread-0begin notify() ThreadName=Thread-1 time=1554183779889  end notify() ThreadName=Thread-1 time=1554183784889  end wait() ThreadName=Thread-0

③中断 调用wait()方法进入等待队列的 线程

Object类中与线程有关的方法:

1)notify/notifyAll

2)wait()/wait(long)

java.lang.Thread中与之相关的方法:

1)interrupt()

2)sleep()/sleep(long)

3)join()/suspend()/resume()....

使用线程的interrupt()方法,可以终止处于阻塞状态中的线程。api介绍如下:

调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“阻塞状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。

实例如下:

1 public class Test { 2  3     public static void main(String[] args) { 4  5         try { 6             Object lock = new Object(); 7  8             ThreadA a = new ThreadA(lock); 9             a.start();10 11             Thread.sleep(5000);12 13             a.interrupt();14         } catch (InterruptedException e) {15             e.printStackTrace();16         }17     }18 }输出结果:java.lang.InterruptedException: wait interrupted	at java.lang.Thread.sleep(Native Method)	at Thread3.run(Thread3.java:13)

④notify 通知的顺序不能错

假设在线程A中执行wait(),在线程B中执行notify()。但如果线程B先执行了notify()然后结束了,线程A才去执行wait(),那此时,线程A将无法被正常唤醒了(还可以通过③中提到的interrupt()方法以抛出异常的方式唤醒)。

⑤多线程中测试某个条件的变化用 if 还是用 while?

以前一直不明白 当在线程的run()方法中需要测试某个条件时,为什么用while,而不用if?

有两个线程从List中删除数据,而只有一个线程向List中添加数据。初始时,List为空,只有往List中添加了数据之后,才能删除List中的数据。添加数据的线程向List添加完数据后,调用notifyAll(),唤醒了两个删除线程,但是它只添加了一个数据,而现在有两个唤醒的删除线程,这时怎么办??

如果用 if 测试List中的数据的个数,则会出现IndexOutofBoundException,越界异常。原因是,List中只有一个数据,第一个删除线程把数据删除后,第二个线程再去执行删除操作时,删除失败,从而抛出 IndexOutofBoundException。

但是如果用while 测试List中数据的个数,则不会出现越界异常.

 

转载于:https://my.oschina.net/xiaoyoung/blog/3031287

你可能感兴趣的文章
Linux发布25周年,正日益庞大和专业
查看>>
我的友情链接
查看>>
中国Linux联盟 - 元旦寄语
查看>>
hive函数详解----条件函数详解
查看>>
遗传算法入门--连载7
查看>>
C语言中的面向对象思想
查看>>
java连接mysql
查看>>
nice graphics of BadCompany2
查看>>
ubuntu10.04 中文乱码 解决
查看>>
Dz经典加密解密方法。
查看>>
iptables(1)
查看>>
常见扫描工具的介绍
查看>>
Win2008R2 删除用户目录后无法生成
查看>>
Redis 配置和启动
查看>>
linux下tree命令
查看>>
最常用的网络应用工具之网线钳
查看>>
01IT技术支持
查看>>
我的友情链接
查看>>
Ubuntu下apt-get与dpkg命令用法
查看>>
Linux基础命令-cd ls alias mv rm tree rmdir mkdir
查看>>