在Java中,可以调用wait()方法使当前线程进入object的等待队列,这样当有新的任务需要执行的时候,调用object的notify()或者notifyAll()方法就可以唤起线程,wait(),notify()在线程池、数据库连接池有有广泛的应用。
其流程为:
首先我们知道,在获取对象的锁,也就是获取到其监视器,获取锁成功之后,则线程进入 RUNNABLE状态,即运行状态,这时候如果调用其对象的wait()方法,即进入对象的等待队列,然后会将锁释放。这时候,别的线程可以获取到这个对象的锁,然后调用其 notifyAll() 方法,这样,所有在等待队列中的线程(即 WAITING 状态)都会进入这个线程的 同步队列,即锁队列。注意 ,调用 notifyAll() 方法 的线程此时可能还并没有释放锁。必须等这个线程释放锁之后,同步队列中的线程才可以一个一个的执行(同步队列中的线程获取到锁之后,必须执行完同步代码块,才会释放锁),也就是说,wait()之后进入到等待队列,notifyAll()后进入锁队列,但只有重新获取到锁之后才能从wait()方法返回,但当wait()的时候线程被打断呢?其实打断的时候,线程也是进入到了锁队列,跟上述一样,而只有当重新获取到锁之后,才会走catch(Execption)的代码块,而不是从wait()方法返回。
有人可能要说了?为什么要这么设计?为什么notify()之后能不能马上从wait()方法返回?为什么还要等再次获取到锁的时候才能返回???
因为只有这样,才能保证在多个线程都在执行Object的同步块的时候:
synchronized (Object) {
}
同一时间,只有一个线程在 synchronized 代码块中!其它的线程要么在Object的等待队列中,要么就是在锁队列中!这样就避免了多个线程在同步块中执行导致的同步问题。
下面举示例来说明:
一、wait()方法与notify()方法
两个等待线程WaitRunnable,一个唤起线程 NotifyAllRunnable,两个等待线程WaitRunnable 等待一段时间之后,唤起线程 NotifyAllRunnable运行,然后调用 NotifyAll方法,然后等待一段时间才释放锁。两个等待线程WaitRunnable在wait()方法唤起之后,又会等待一段时间,那么,谁先运行完?谁再运行完呢?
public class Main {
public static final Object waitObject = new Object();
public static void main(String[] args) {
Thread waitThread1 = new Thread(new WaitRunnable(), "waitThread1");
Thread waitThread2 = new Thread(new WaitRunnable(), "waitThread2");
waitThread1.start();
waitThread2.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread notifyAllThread = new Thread(new NotifyAllRunnable(), "notifyAllThread");
notifyAllThread.start();
}
}
WaitRunnable:
public class WaitRunnable implements Runnable{
@Override
public void run() {
synchronized (Main.waitObject) {
System.out.println(Thread.currentThread().getName() + " 开始wait ");
try {
/**
* 会释放锁
*/
Main.waitObject.wait();
/**
* 又拿到锁
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " wait结束,又获取到锁 ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " wait sleep 结束,释放锁 ");
/**
* 即将释放锁
*/
}
/**
* 所以上述代码块是 先拿到锁,释放,然后又拿到了锁,再释放
*/
System.out.println(Thread.currentThread().getName() + " wait结束代码块 \n\n\n");
}
}
NotifyAllRunnable:
public class NotifyAllRunnable implements Runnable{
@Override
public void run() {
synchronized (Main.waitObject) {
System.out.println("NotifyAllRunnable begin");
Main.waitObject.notifyAll();
System.out.println("NotifyAllRunnable notify");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("NotifyAllRunnable 即将释放锁");
}
System.out.println("NotifyAllRunnable 释放锁完毕 \n\n\n");
}
}
执行结果为:
waitThread1 开始wait
waitThread2 开始wait
NotifyAllRunnable begin
NotifyAllRunnable notifyAll
NotifyAllRunnable 即将释放锁
NotifyAllRunnable 释放锁完毕
waitThread2 wait结束,又获取到锁
waitThread2 wait sleep 结束,释放锁
waitThread2 wait结束代码块
waitThread1 wait结束,又获取到锁
waitThread1 wait sleep 结束,释放锁
waitThread1 wait结束代码块
可以看到,NotifyAllRunnable 虽然已经notifyAll()来通知其它线程了,但是其他等待线程只是由等待队列进入了锁队列,NotifyAllRunnable还持有锁,所以在其 sleep 了一段时间之后,释放了锁。waitThread才能获取到锁,注意其中一个 waitThread A 获取到锁之后,并未马上释放,而是也等待了一段时间,另一个 waitThread B 依旧在锁队列。只有在 waitThread A 执行完同步代码块之后,才会把这个对象的锁释放,waitThread B才会获取到锁。所以我们notify()或者notifyAll()后wait的线程可能不会马上执行。
二、wait(long,int)方法:
这个方法有点意思了就,wait(long,int)之后,进入等待队列,注意如果在指定时间内收到了notifyAll,那么会跟上述一样进入锁队列,否则会在超时之后进入到锁队列中。
wait(long,int),第一个参数是要等到的毫秒数,第二个是纳秒数,第二个是为了更精确,指定为1即可,
比如,想等待2秒,即2000ms,wait(2000, 1) 即可。
public class WaitTimeMain {
public static final Object waitTimeObject = new Object();
public static void main(String[] args) {
Thread waitTimeThread = new Thread(new WaitTimeRunnable(), "waitTimeThread");
waitTimeThread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread syncThread = new Thread(new SyncRunnable(), "SyncThread");
syncThread.start();
}
}
public class WaitTimeRunnable implements Runnable{
@Override
public void run() {
synchronized (WaitTimeMain.waitTimeObject) {
long beginTime = System.currentTimeMillis();
System.out.println(" WaitTimeRunnable wait开始,当前时间" + beginTime);
try {
WaitTimeMain.waitTimeObject.wait(2000, 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println(" WaitTimeRunnable wait完毕 , 用时 "
+ (endTime - beginTime) + "ms");
}
}
}
public class SyncRunnable implements Runnable{
@Override
public void run() {
synchronized (WaitTimeMain.waitTimeObject) {
System.out.println(" SyncRunnable 获取到锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" SyncRunnable 5秒之后释放锁 \n\n\n");
}
}
}
执行结果为:
WaitTimeRunnable wait开始
SyncRunnable 获取到锁
SyncRunnable 5秒之后释放锁
WaitTimeRunnable wait完毕 , 用时 5112ms
可以看到,虽然WaitTimeRunnable没有收到通知,2s之后自动进入锁队列,但是现在已有线程获取到锁,其要等待锁释放,即5秒之后才完成。