使用的时候要注意,要调用共享变量对象的wait()方法,必须先获取该对象的Monitor。否则会抛出IllegalMonitorStateException;
当调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起,直到:1)其他线程调用了该共享变量的notify()或notifyAll()方法;2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回。
import java.util.PriorityQueue; import com.tim.base.easystart.util.VeDate; /** * 1, wait使用之前需要获取到操作对象的Monitor * 2, wait之后会释放锁定的Monitor * 3, notifyAll会唤醒等待该对象的Monitor的所有线程,具体谁来获取到这个Monior是随机的 */ public class WaitTest { static PriorityQueue<String> queue=new PriorityQueue<String>(); public static void add(String content) throws InterruptedException { synchronized (queue) { while (queue.size() > 10) { System.out.println("The Queue is Over 10..."); queue.wait(); } queue.add(content); queue.notifyAll(); } } public static String getQueue() { synchronized (queue) { while (queue.size() == 0) { try { System.out.println("The Queue is Empty..."); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } String result = queue.poll(); queue.notifyAll(); return result; } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { while(true) { try { add(VeDate.getNo(5)); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true) { System.out.println(getQueue()); } } }); t1.start(); t2.start(); } } //附上工具方法 import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; public final class VeDate { private VeDate() { } /** * 根据用户传入的时间表示格式,返回当前时间的字符串 * @param sformat * yyyyMMddhhmmss * @return */ public static String getUserDate(String sformat) { Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat(sformat); String dateString = formatter.format(currentTime); return dateString; } /** * 生成格式为yyyymmddhhmmss+k位随机数 * * @param k * 表示是取几位随机数,可以自己定 * @return */ public static String getNo(int k) { return getUserDate("yyyyMMddHHmmss") + getRandom(k); } /** * 返回一个随机数 * * @param i * @return */ public static String getRandom(int i) { Random jjj = new Random(); if (i == 0) { return ""; } StringBuffer jj = new StringBuffer(); for (int k = 0; k < i; k++) { jj.append(jjj.nextInt(9)); } return jj.toString(); } }wait()方法的常见使用方式:
synchronized (obj) { whie(条件不满足) { obj.wait(); } }不使用while来判断条件,可能会出现虚假唤醒,关于虚假唤醒可以点击查看wait()-虚假唤醒
当前线程调用共享变量的wait()方法之后,只会释放当前共享变量上的锁,如果当前线程还持有其他共享变量的锁,则这些锁是不会被释放的。
wait(long timeout)比wait()函数多了一个超时参数,如果一个线程调用共享对象的wait(long timeout)方法后,没有在timeout ms时间内,被其它线程调用该变量的notify()或notifyAll()方法唤醒,那么函数还是会因为超时而返回。
timeout设置为0效果等同于wait();
public final void wait() throws InterruptedException { wait(0); }timeout设置小于0会抛出IllegalArgumentException异常
/** * @param timeout the maximum time to wait in milliseconds. * @throws IllegalArgumentException if the value of timeout is * negative. * @throws IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final native void wait(long timeout) throws InterruptedException;wait(long timeout, int nanos)
public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); }一个线程调用共享对象的notify()方法之后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。如果有多个线程都调用wait方法,那么具体唤醒哪个线程是随机的。
被唤醒后不能立刻从wait方法返回并继续执行,它必须获取共享对象的监视器锁之后才可以返回。所以说唤醒它的线程释放了共享变量的监视器锁之后,被唤醒的线程也不一定会获取到共享对象的监视器锁,该线程必须和其它线程一起竞争这个锁,只有竞争到共享变量的监视器锁之后才可以继续执行。
调用该方法之前,该线程必须获取到共享对象的监视器锁,这个要求和wait()方法是一致的,否则会抛出InterruptedException异常。
一个线程调用共享对象的notifyAll()方法之后,会唤醒所有由于调用该共享对象wait系列方法被阻塞的所有线程。
public class NotifyTest { private static volatile Object resourceA = new Object(); public static void main(String[] args) throws InterruptedException { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { System.out.println("threadA get resourceA lock"); try { System.out.println("threadA begin wait"); resourceA.wait(); System.out.println("threadA end wait"); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { System.out.println("threadB get resourceA lock"); try { System.out.println("threadB begin wait"); resourceA.wait(); System.out.println("threadB end wait"); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread threadC = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { System.out.println("threadC begin notify"); resourceA.notify(); } } }); //启动线程 threadA.start(); threadB.start(); Thread.sleep(1000); //等待线程A和B都执行到wait方法 threadC.start(); //等待线程结束 threadA.join(); threadB.join(); threadC.join(); System.out.println("main over"); } }输出:
threadB get resourceA lock threadB begin wait threadA get resourceA lock threadA begin wait threadC begin notify threadB end wait
最终只有一个线程被唤醒,如果将resourceA.notify();修改为resourceA.notifyAll();
输出:
threadB get resourceA lock threadB begin wait threadA get resourceA lock threadA begin wait threadC begin notify threadA end wait threadB end wait main over
从结果还可以理解下,线程A和B都被唤醒了,但是线程A先竞争到了resourceA的监视器锁,所以先执行完毕了,释放了监视器锁之后,线程B也拿到了这个Monitor,然后紧接着执行完毕,最终主线程执行完毕。
关于join方法我们后续再讨论。