章
目
录
本文主要重点讲解Java多线程:线程间通信(等待/通知机制)问题。
等待/通知机制概念
等待/通知机制在我们生活中比比皆是,一个形象的例子就是厨师和服务员之间就存在等待/通知机制。
- 厨师做完一道菜的时间是不确定的,所以菜到服务员手中的时间是不确定的;
- 服务员就需要去“等待(wait)”;
- 厨师把菜做完之后,按一下铃,这里的按铃就是“通知(nofity)”;
- 服务员听到铃声之后就知道菜做好了,他可以去端菜了。
在Java多线程中理解就是:
等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()/notifyAll()
方法,线程A收到通知后退出等待队列,进入可运行状态,进而执行后续操作。上面这样的一个过程就是等待/通知机制。
等待/通知机制实现
Object类中的wait()方法,可以使执行当前代码的线程等待,暂停执行。直到接到通知或被中断为止。
Object类的notify()方法可以唤醒处于等待的线程,该方法也必须在同步代码块中由锁对象调用。如果有多个等待的线程,notify()只能唤醒其中一个,具体唤醒哪一个是不知道的。被notify()的线程需要重新去竞争锁才能被执行。
没有使用锁对象就调用wait()/notify()方法会产生异常:IllegalMonitorStateException。
wait()代码实例:
public class Test01 {
public static void main(String[] args) throws InterruptedException {
String test = "abc";
String another = "def";
System.out.println("同步代码块前的代码");
synchronized (test) {
try {
System.out.println("wait前的代码");
// another.wait(); 只有被锁住的对象才能调用wait()方法
test.wait();
System.out.println("wait后的代码");
} catch (IllegalMonitorStateException e) {
e.printStackTrace();
}
}
System.out.println("同步代码块后的代码");
}
}
wait()/notify()代码示例:
/**
* 需要通过notify唤醒线程
*/
public class Test02 {
public static void main(String[] args) {
String str = "wa";
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (str) {
System.out.println("线程1开始等待");
try {
str.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1被唤醒并执行结束了");
}
}
}, "Thread1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (str) {
System.out.println("线程2唤醒线程1");
str.notify();
}
}
}, "Thread2");
thread1.start();
thread2.start();
}
}
执行了notify()的线程并不会立即释放锁,而是执行完同步代码块的所有代码后才会释放锁
interrupt()方法会中断wait()
当线程调用wait()处于等待状态时,调用线程对象的interrupt()方法会中断线程的等待状态,产生InterruptedException异常。
/**
* interrupt()会中断线程的wait状态
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
SubThread subThread = new SubThread();
subThread.start();
TimeUnit.SECONDS.sleep(1);
subThread.interrupt();
}
private static final Object lock = new Object();
static class SubThread extends Thread {
@Override
public void run() {
synchronized (lock) {
System.out.println("subThread wait");
try {
lock.wait();
} catch (InterruptedException e) {
System.out.println("wait等待被中断了");
}
System.out.println("subThread end wait");
}
}
}
}
wait(long)方法的使用
如果在指定时间内没有被唤醒,那么线程会自动唤醒。
public class Test06 {
public static void main(String[] args) {
SubThread subThread = new SubThread();
subThread.start();
}
static final Object lock = new Object();
static class SubThread extends Thread{
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("开始等待");
lock.wait(5000);
System.out.println("等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
notify()和notifyAll()的区别
1) 唤醒数量不同
notify()方法只会随机唤醒等待队列中的一个线程,而notifyAll()方法则会唤醒等待队列中的所有线程。
2)调用方式不同
notify()和notifyAll()方法都必须在同步代码块中调用,并且必须包含在synchronized块中,且必须是该对象的监视器对象才能够调用。而且只有在获取了锁之后才能调用,否则会抛出IllegalMonitorStateException异常。
3) 竞争情况不同
notify()方法只会唤醒等待队列中的一个线程,并使其与其他线程竞争获取锁,这可能会导致某些线程无法被唤醒或者一直处于等待状态。
notifyAll()方法则会唤醒等待队列中的所有线程,并使它们竞争获取锁,这样可以使所有线程都有机会获取锁并进入运行状态,从而避免了一些线程一直处于等待状态。
总结
以上就是Java多线程:线程间通信(等待/通知机制)的基础内容,如下想要更深层次地理解该机制,可以参考下一篇文章:Java多线程:线程间通信(生产者消费者模式)