目录
概述 与 问答
1、wait()、notify() 和 notifyAll() 是 Java 超级根类 Object 类中的方法
2、wait()、notify() 和 notifyAll() 方法为 final 修饰,无法被重写
3、调用某个对象的 wait() 方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁(monitor)
4、调用某个对象的 notify() 方法能够唤醒一个正在等待这个对象锁线程,如果有多个线程都在等待这个对象的锁,则随机唤醒其中一个线程;
5、调用某个对象的 notifyAll() 方法能够唤醒所有正在等待这个对象的 monitor 的线程;
常用方法 | 描述 |
public final native void wait() public final native void wait(long timeout) | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 wait在等待的时候会释放锁,而Thread的sleep休眠的时候是不会释放锁的。 |
public final native void notify() | 唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生 |
public final native void notifyAll() | 唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 |
为什么wait、notify、notifyAll方法不在thread类里面?
1、主要原因是 Java 提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。
2、由于 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object 类中,因为锁属于对象。
为什么wait、notify、notifyAll方法要在同步块中调用?
1、Java API 强制要求 wait,notify 和 notifyAll 必须写在 synchronized 同步代码块中,如果不这么做,代码会抛出java.lang.IllegalMonitorStateException 异常。
2、还有一个原因是为了避免 wait 和 notify 之间产生竞态条件。
java中wait和sleep方法的不同?
1、最大的不同是在等待是 wait 会释放锁,而 sleep 一直持有锁。
2、Wait 通常被用于线程间交互,sleep通常被用于暂停执行。
3、wait()方法会释放 CPU执行权和占有的锁。
4、sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与Thread.yield() 相比,它会使线程较长时间得不到运行。
5、Thread.yield() 方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。
6、wait 和 notify、notifyAll 必须配套使用,即必须使用同一把锁调用;
7、wait和notify必须放在一个同步块中调用,wait和notify的对象必须是他们所处同步块的锁对象。
wait + notify 举例
import java.util.logging.Logger;
/**
* Created by Administrator on 2019/3/11 0011.
*/
public class NotifyAllTest {
public static final Logger logger = Logger.getAnonymousLogger();//日志处理器
public static void main(String[] args) throws InterruptedException {
NotifyAllTest notifyAllTest = new NotifyAllTest();
/**
* 创建一个子线程并启动它
* 因为要保证调用 wait 与 调用 notify、notifyAll的是同一个对象,所以这里将 notifyAllTest 实例作为参数传过去
* 提醒:调用 wait 与 调用 notify、notifyAll 必须是同一个对象,但可以是任意对象,比如普通的 String 、Array 、Boolean 等等
*/
MyThread myThread_1 = new MyThread(notifyAllTest);
myThread_1.start();
for (int i = 3; i > 0; i--) {
Thread.sleep(1000);
logger.info("线程 " + Thread.currentThread().getName() + " " + (i) + " 秒后唤醒子线程");
}
//Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
//如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
synchronized (notifyAllTest) {
notifyAllTest.notify();
}
}
}
class MyThread extends Thread {
private static final Logger logger = Logger.getAnonymousLogger();//日志处理器
private NotifyAllTest notifyAllTest;
public MyThread(NotifyAllTest notifyAllTest) {
this.notifyAllTest = notifyAllTest;
}
@Override
public void run() {
//Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
//如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
synchronized (notifyAllTest) {
try {
logger.info("线程 " + currentThread().getName() + " 开始阻塞等待...");
notifyAllTest.wait();
logger.info("线程 " + currentThread().getName() + " 被重新唤醒...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
================控制台输出如下=================
三月 11, 2019 11:35:30 上午 com.xyjt.notify.MyThread run
信息: 线程 Thread-1 开始阻塞等待...
三月 11, 2019 11:35:31 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 3 秒后唤醒子线程
三月 11, 2019 11:35:32 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 2 秒后唤醒子线程
三月 11, 2019 11:35:33 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 1 秒后唤醒子线程
三月 11, 2019 11:35:33 上午 com.xyjt.notify.MyThread run
信息: 线程 Thread-1 被重新唤醒...
wait + notifyAll 举例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
* Created by Administrator on 2019/3/11 0011.
* @author wangmaoxiong
*/
public class NotifyAllTest {
/**
* logger:日志处理器
* isWait:作为所有线程 wait 等待与 notify、notifyAll 通知的同一对象
*/
public static final Logger logger = Logger.getAnonymousLogger();
public static final Boolean isWait = true;
public static void main(String[] args) throws InterruptedException {
/**
* 使用无界线程池创建多个子线程并启动它们
* 调用 wait 与 调用 notify、notifyAll 必须是同一个对象,但可以是任意对象,比如普通的 String 、Array 、Boolean 等等
*/
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new MyThread(isWait));
}
for (int i = 5; i > 0; i--) {
Thread.sleep(1000);
logger.info("线程 " + Thread.currentThread().getName() + " " + (i) + " 秒后唤醒子线程");
}
//Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
//如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
synchronized (isWait) {
isWait.notifyAll();//通知isWait对象的所有线程继续向后运行
}
}
}
class MyThread extends Thread {
private static final Logger logger = Logger.getAnonymousLogger();
private Boolean isWait;
public MyThread(Boolean isWait) {
this.isWait = isWait;
}
@Override
public void run() {
//Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
//如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
synchronized (isWait) {
try {
logger.info("线程 " + currentThread().getName() + " 开始阻塞等待...");
isWait.wait();
logger.info("线程 " + currentThread().getName() + " 被重新唤醒...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1、wait 在等待的时候会释放锁,所以上面当第一个线程在 wait 在等待的时候,其它线程立马就能获取锁而进来。
2、当所有的线程都释放了锁在 wait 等待的时候,如果 main 方法此时使用 notifyAll 方法通过大家继续运行的时候,这个时候大家如何重新获取锁按顺序运行呢?它们会再次争夺锁确保了至少有一个线程能继续运行。