1. wait(),notify(),notifyAll()方法。
wait()、notify()和notifyAll()这三个方法都依赖于Java对象的监视器锁(也称为内置锁或互斥锁)。调用这些方法时,当前线程必须持有该对象的监视器锁,所以通常它们三都与synchronized 关键字一起使用,否则将抛出IllegalMonitorStateException异常(也就是没有获取对象的监视器锁和调用了这些方法)。这里我们以wait(),notify(),notifyAll()为主线,然后穿插对于sleep的区别和interrupt的应用。
1.1. wait()方法
1.1.1 释放锁
当线程调用对象的wait()方法时,它会释放该对象的监视器锁。这允许其他线程能够进入同步代码块或方法,并获得锁以执行其操作。(与此相反sleep方法不会释放监视器锁)
如下:
- 我们将第一个线程Chinese先启动后,
- 再将主线程sleep睡2s确保Chinese一定是在第二个线程Math前启动的。
- 但是是我们在第一个线程Chinese中等待5s,同时
- 启动第二个线程,可以看到第二个线程Match的结果是先被输出的,所以wait方法会释放锁
public class studyClass { public static void main(String[] args) { Object object = new Object(); Chinese chinese = new Chinese(object); Math math = new Math(object); Thread tChi = new Thread(chinese); Thread tMatch = new Thread(math); tChi.start(); try{ Thread.sleep(2000); }catch (InterruptedException ex){ ex.printStackTrace(); } tMatch.start(); } } //--------------------------------------------------------- class Chinese implements Runnable{ private Object object; public Chinese(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<C "); System.out.print("C> "); } } } //--------------------------------------------------------- class Math implements Runnable{ private Object object; public Math(Object object){ this.object = object; } @Override public void run() { synchronized (object) { System.out.print("<M "); System.out.print("M> "); } } }
object.wait(5000); 的结果
如果我们将Chinese 子线程中的的object.wait(5000); 改为Thread.sleep(5000);得到的结果为如下,说明sleep方法是不会释放锁的。
1.1.2. 等待与通知
wait()方法通常与notify()或notifyAll()方法一起使用,以在多个线程之间实现协作。一个线程可以调用wait()来等待某个条件成立,而另一个线程可以在条件成立后调用notify()或notifyAll()来唤醒在等待的线程。
如下:
- 我们将第一个子线程Chinese先启动
- 主线程先睡1s后
- 再启动第二个线程Match,确保第一个线程启动后一定就进入wait。
- 可以看到输出结果是第二个线程Match的,在它输出完成后调用了notify()后。
- 第一个线程的wait才结束的并输出第一个线程的结果。
public class studyClass { public static void main(String[] args) { Object object = new Object(); Chinese chinese = new Chinese(object); Math math = new Math(object); Thread tChi = new Thread(chinese); Thread tMatch = new Thread(math); tChi.start(); try{ Thread.sleep(1000); }catch (InterruptedException ex){ ex.printStackTrace(); } tMatch.start(); } } //--------------------------------------------------------- class Chinese implements Runnable{ private Object object; public Chinese(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<C "); System.out.print("C> "); } } } //--------------------------------------------------------- class Math implements Runnable{ private Object object; public Math(Object object){ this.object = object; } @Override public void run() { synchronized (object) { System.out.print("<M "); System.out.print("M> "); object.notify(); } } }
1.1.3. 可中断
调用wait()方法等待的线程可以通过调用其interrupt()方法被中断。如果线程在等待时被中断,wait()方法将抛出InterruptedException异常。
如下:
- 方法我们在第一个子线程Chinese进入wait(5000)
- 紧接着在主方法中让第一个子线程Chinese中断
- 测试结果还是立马会中断并继续执行未执行完的代码,而不会继续wait直到5s,可与1.1.1对比可知。
(wait方法会释放锁,中断后立马再次获取锁,然后在执行接下来的代码,sleep的中断和wait类似,这里不在举例)public class studyClass { public static void main(String[] args) { Object object = new Object(); Chinese chinese = new Chinese(object); Math math = new Math(object); Thread tChi = new Thread(chinese); Thread tMatch = new Thread(math); tChi.start(); tChi.interrupt(); tMatch.start(); } } //--------------------------------------------------------- class Chinese implements Runnable{ private Object object; public Chinese(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<C "); System.out.print("C> "); } } } //--------------------------------------------------------- class Math implements Runnable{ private Object object; public Math(Object object){ this.object = object; } @Override public void run() { synchronized (object) { System.out.print("<M "); System.out.print("M> "); } } }
1.2. notify(),notifyAll()方法
1.2.1. 等待队列与同步队列:
notify(或notifyAll)方法会将等待队列中的一个(或多个)线程移到同步队列中,但并不立即将其激活。只有当该线程重新获取到对象锁后,它才会真正从wait()方法返回并继续执行。
如下我们将
- 第一个子线程Chinse先启动,但启动后进入wait状态
- 第二个子线程Match再启动,
- 第二个子线程启动后输出一个<M后立马notify,但是nofity后面还需要输出10万次M
- 我们可以看到结果,第二个子线程并没有立马将第一个子线程激活而是将自身的事情做完了(输出了10万个M) 才释放的锁并让第一个子线程执行。所以nofity或nofityAll并不会释放锁。
public class studyClass { public static void main(String[] args) { Object object = new Object(); Chinese chinese = new Chinese(object); Math math = new Math(object); Thread tChi = new Thread(chinese); Thread tMatch = new Thread(math); tChi.start(); try{ Thread.sleep(1000); }catch (InterruptedException ex){ ex.printStackTrace(); } tMatch.start(); } } //--------------------------------------------------------- class Chinese implements Runnable{ private Object object; public Chinese(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<C "); System.out.print("C> "); } } } //--------------------------------------------------------- class Math implements Runnable{ private Object object; public Math(Object object){ this.object = object; } @Override public void run() { synchronized (object) { System.out.print("<M "); object.notify(); for (int i = 0; i<100000;i++){ System.out.print("M"); } System.out.print("M> "); } } }
1.2.2. 随机性:
notify()方法随机唤醒等待队列中的一个线程,所以你不能控制具体哪个线程会被唤醒。如果你需要唤醒所有等待的线程,应该使用notifyAll()方法。
1.2.3 notify(),notifyAll()区别
notfiy 随机通知其中一个线程,notifyAll同时所以线程。
如下有三个子线程 Chinese、Math、English。
- 我们先执行第一个子线程Chinese,并让他进入等待状态。
- 再执行第二个子线程Match,同样让它进入等待状态。
- 再执行地三个子线程English但不wait,等它执行完后再调用notify()或notifyAll()方法。
public class studyClass { public static void main(String[] args) { Object object = new Object(); Chinese chinese = new Chinese(object); Math math = new Math(object); English english = new English(object); Thread tChi = new Thread(chinese); Thread tMatch = new Thread(math); Thread tEng = new Thread(english); tChi.start(); tMatch.start(); try{ Thread.sleep(1000); }catch (InterruptedException ex){ ex.printStackTrace(); } tEng.start(); } } //--------------------------------------------------------- class Chinese implements Runnable{ private Object object; public Chinese(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<C "); System.out.print("C> "); } } } //--------------------------------------------------------- class Math implements Runnable{ private Object object; public Math(Object object){ this.object = object; } @Override public void run() { synchronized (object) { try{ object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print("<M "); System.out.print("M> "); } } } //--------------------------------------------------------- class English implements Runnable{ private Object object; public English(Object object){ this.object = object; } @Override public void run() { synchronized (object) { System.out.print("<E "); object.notify(); System.out.print("E> "); } } }
notfiy()方法的结果。永远是通知其中一个wait方法,而另外一个wait方法还是在wait下去
将代码中的object.notify();改为object.notifyAll();