[java多线程]多线程同步(二)——wait, notify, notifyAll, join以及sleep
来源:互联网 发布:linux java dump命令 编辑:程序博客网 时间:2024/05/31 13:16
我们先来看一下Object类中wait, notify和notifyall的定义:
public class Object { private static native void registerNatives(); static { registerNatives(); } /** * Wakes up a single thread that is waiting on this object's * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the <code>wait</code> methods. * <p> * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. A thread becomes the owner of the * object's monitor in one of three ways: * <ul> * <li>By executing a synchronized instance method of that object. * <li>By executing the body of a <code>synchronized</code> statement * that synchronizes on the object. * <li>For objects of type <code>Class,</code> by executing a * synchronized static method of that class. * </ul> * <p> * Only one thread at a time can own an object's monitor. * * @exception IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ public final native void notify(); /** * Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the * <code>wait</code> methods. * <p> * The awakened threads will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened threads * will compete in the usual manner with any other threads that might * be actively competing to synchronize on this object; for example, * the awakened threads enjoy no reliable privilege or disadvantage in * being the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. See the <code>notify</code> method for a * description of the ways in which a thread can become the owner of * a monitor. * * @exception IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notify() * @see java.lang.Object#wait() */ public final native void notifyAll(); /** * Causes the current thread to wait until either another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object, or a * specified amount of time has elapsed. * <p> * The current thread must own this object's monitor. * <p> * This method causes the current thread (call it <var>T</var>) to * place itself in the wait set for this object and then to relinquish * any and all synchronization claims on this object. Thread <var>T</var> * becomes disabled for thread scheduling purposes and lies dormant * until one of four things happens: * <ul> * <li>Some other thread invokes the <tt>notify</tt> method for this * object and thread <var>T</var> happens to be arbitrarily chosen as * the thread to be awakened. * <li>Some other thread invokes the <tt>notifyAll</tt> method for this * object. * <li>Some other thread {@linkplain Thread#interrupt() interrupts} * thread <var>T</var>. * <li>The specified amount of real time has elapsed, more or less. If * <tt>timeout</tt> is zero, however, then real time is not taken into * consideration and the thread simply waits until notified. * </ul> * The thread <var>T</var> is then removed from the wait set for this * object and re-enabled for thread scheduling. It then competes in the * usual manner with other threads for the right to synchronize on the * object; once it has gained control of the object, all its * synchronization claims on the object are restored to the status quo * ante - that is, to the situation as of the time that the <tt>wait</tt> * method was invoked. Thread <var>T</var> then returns from the * invocation of the <tt>wait</tt> method. Thus, on return from the * <tt>wait</tt> method, the synchronization state of the object and of * thread <tt>T</tt> is exactly as it was when the <tt>wait</tt> method * was invoked. * <p> * A thread can also wake up without being notified, interrupted, or * timing out, a so-called <i>spurious wakeup</i>. While this will rarely * occur in practice, applications must guard against it by testing for * the condition that should have caused the thread to be awakened, and * continuing to wait if the condition is not satisfied. In other words, * waits should always occur in loops, like this one: * <pre> * synchronized (obj) { * while (<condition does not hold>) * obj.wait(timeout); * ... // Perform action appropriate to condition * } * </pre> * (For more information on this topic, see Section 3.2.3 in Doug Lea's * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming * Language Guide" (Addison-Wesley, 2001). * * <p>If the current thread is {@linkplain java.lang.Thread#interrupt() * interrupted} by any thread before or while it is waiting, then an * <tt>InterruptedException</tt> is thrown. This exception is not * thrown until the lock status of this object has been restored as * described above. * * <p> * Note that the <tt>wait</tt> method, as it places the current thread * into the wait set for this object, unlocks only this object; any * other objects on which the current thread may be synchronized remain * locked while the thread waits. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. See the <code>notify</code> method for a * description of the ways in which a thread can become the owner of * a monitor. * * @param timeout the maximum time to wait in milliseconds. * @exception IllegalArgumentException if the value of timeout is * negative. * @exception IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @exception 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; 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 >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); }}
notify的javadoc说了一个很重要的内容,
一个线程要成为一个对象的监控器(内在锁)的拥有者,有以下三种方式:
1. 执行那个对象的synchronized实例方法;
2. 执行synchronized块,以那个对象为同步对象;
3. 对于类对象(java.lang.Class),执行那个类的synchronized静态方法;
同一时刻,只有一个线程拥有一个对象的监控器(内在锁)。
ps:我觉得内在锁更容易理解,后面我都用这个名词。
一、wait()方法
从代码可以看出可以看出wait()方法其实就是调用的wait(0),而wait(long timeout)的作用是使当前线程进入等待状态,直到另一个线程为某个对象调用notify或notifyAll方法,或者超过指定时间。
当前线程必须拥有调用wait()方法的对象的内在锁,javadoc中有下面的代码,很明显这个是用的获取对象内在锁的第2种方式。
synchronized (obj) { while (<condition does not hold>) obj.wait(timeout); ... // Perform action appropriate to condition}这个方法使得当前线程(比方说是线程T)将自身置于调用wait方法对象的wait set,并释放同步对象的内在锁。
注意,当前线程在等待时,线程中其它需要同步的对象仍然处于加锁状态;
线程T不能线程调度,并且在直到下面四种情况发生前都会处于休眠状态:
1. 其它线程调用等待对象的notify方法,并且线程T碰巧被选中为唤醒线程;
2. 其它线程调用等待对象的notifyAll方法;
3. 其它线程中断了线程T;
4. 指定的超时时间已经过了。然而如果超时时间设置为0,时间将不作考虑,线程在被notify之前只会一直等待;
线程还可能被虚假唤醒,虽然实际中很少发生,但在应用中对唤醒线程进行测试,如果条件不满足,必须使虚假唤醒的线程继续等待。所以wait()方法总是在循环中执行。
线程T从wait set中移除,并且可以进行线程调度。它和其它线程一样竞争同步使用对象的权利。线程T从wait()方法请求中返回时,同步对象和线程T的状态都完全和wait()方法被调用时一样。
二、notify()方法
唤醒一个等待对象内在锁的线程。如果有多个线程等待对象内部锁,其中一个被选择随机的唤醒。
被唤醒的线程不会执行,直到当前线程交出对象的内在锁。被唤醒的线程将和其它线程完全平等的竞争对象的同步块访问。
我们先来看一个wait()和notify()的例子,典型的生产者/消费者:
三、notifyAll()方法
唤醒等待对象内在锁的所有线程。
唤醒线程不会被执行,直到当前线程交出对象的内在锁。被唤醒的线程将和其它线程完全平等的竞争对象的同步块访问。
四、一个例子
public class MyProduct {private String name;private int productIdx;public MyProduct(String name, int productIdx) {this.name = name;this.productIdx = productIdx;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getProductIdx() {return productIdx;}public void setProductIdx(int productIdx) {this.productIdx = productIdx;}@Overridepublic String toString() {return "MyProduct [name=" + name + ", productIdx=" + productIdx + "]";}}
/** * 模拟工厂,制造产品放到产品队列中 * * 会根据产品队列中产品数量来进行生产控制,当产品数大于MAX_PRODUCTS就停止生产 */public class FactoryThread implements Runnable {private Queue<MyProduct> productQueue;public static final int MAX_PRODUCTS = 10;public FactoryThread(Queue<MyProduct> productQueue) {this.productQueue = productQueue;}@Overridepublic void run() {if (null == productQueue) {return;}String threadName = Thread.currentThread().getName();int productIdx = 1; // 产品编号while (true) {if (produceProducts(threadName, productIdx)) {break;}productIdx++;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}private boolean produceProducts(String threadName, int productIdx) {boolean isInterrupted = false;synchronized (productQueue) {// 如果队列中产品太多,等待顾客消费后通知工厂while (productQueue.size() >= MAX_PRODUCTS) {try {productQueue.wait();} catch (InterruptedException e) {isInterrupted = true;break;}}if (!isInterrupted) {MyProduct p = new MyProduct(threadName, productIdx);productQueue.offer(p);System.out.println("FactoryThread: " + p + "; productQueue.size(): " + productQueue.size());productQueue.notifyAll();}}return isInterrupted;}}
/** * 模拟客户,从产品队列中得到产品 * * 会根据产品队列中产品数量来进行消费控制,当产品数小于MIN_PRODUCTS就等待生产 * */public class CustomerThread implements Runnable {private Queue<MyProduct> productQueue;public static final int MIN_PRODUCTS = 5;public CustomerThread(Queue<MyProduct> productQueue) {this.productQueue = productQueue;}@Overridepublic void run() {if (null == productQueue) {return;}String threadName = Thread.currentThread().getName();while (true) {if (consumeProducts(threadName)) {break;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}private boolean consumeProducts(String threadName) {boolean isInterrupted = false;synchronized (productQueue) {// 如果队列中产品太少,等待工厂生产后通知顾客while (productQueue.size() <= MIN_PRODUCTS) {try {productQueue.wait();} catch (InterruptedException e) {isInterrupted = true;break;}}if (!isInterrupted) {MyProduct p = productQueue.poll();System.out.println("CustomerThread: " + threadName + " buy " + p + "; productQueue.size(): "+ productQueue.size());productQueue.notifyAll();}}return isInterrupted;}}
import java.util.concurrent.ConcurrentLinkedQueue;public class Test {public static void main(String[] args) throws InterruptedException {ConcurrentLinkedQueue<MyProduct> productQueue = new ConcurrentLinkedQueue<MyProduct>();for (int i = 0; i < 5; i++) {new Thread(new FactoryThread(productQueue)).start();}for (int i = 0; i < 5; i++) {new Thread(new CustomerThread(productQueue)).start();}}}
再来看join,在sdk的Tread类里有如下代码:
/** * Waits for this thread to die. * * @exception InterruptedException if any thread has interrupted * the current thread. The <i>interrupted status</i> of the * current thread is cleared when this exception is thrown. */ public final void join() throws InterruptedException {join(0); } /** * Waits at most <code>millis</code> milliseconds for this thread to * die. A timeout of <code>0</code> means to wait forever. * * @param millis the time to wait in milliseconds. * @exception InterruptedException if any thread has interrupted * the current thread. The <i>interrupted status</i> of the * current thread is cleared when this exception is thrown. */ public final synchronized void join(long millis) throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) { throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) { while (isAlive()) {wait(0); }} else { while (isAlive()) {long delay = millis - now;if (delay <= 0) { break;}wait(delay);now = System.currentTimeMillis() - base; }} } /** * Waits at most <code>millis</code> milliseconds plus * <code>nanos</code> nanoseconds for this thread to die. * * @param millis the time to wait in milliseconds. * @param nanos 0-999999 additional nanoseconds to wait. * @exception IllegalArgumentException if the value of millis is negative * the value of nanos is not in the range 0-999999. * @exception InterruptedException if any thread has interrupted * the current thread. The <i>interrupted status</i> of the * current thread is cleared when this exception is thrown. */ public final synchronized void join(long millis, int nanos) throws InterruptedException {if (millis < 0) { throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++;}join(millis); }
代码说明了几点:
1、判断一个线程是否还活着,用的是isAlive()方法,并且这个方法调用后,所在线程处于阻塞中;
2、有三种方式可以让join从阻塞中跳出:
a. 所等待的线程正常执行完毕;
b. 所等待的线程被中断(Interrupt);
c. 设置的等待时间已过;
这边有一个简单的例子:
子线程
import java.util.concurrent.atomic.AtomicInteger;public class MyThread implements Runnable {private AtomicInteger num;public MyThread(AtomicInteger num) {this.num = num;}@Overridepublic void run() {String threadName = Thread.currentThread().getName();for (int i = 0; i < 10; i++) {System.out.println(threadName + ": i = " + i + ", num = " + num.getAndDecrement());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}父线程
import java.util.concurrent.atomic.AtomicInteger;public class Test {public static void main(String[] args) {MyThread threadOne = new MyThread(new AtomicInteger(20));Thread t1 = new Thread(threadOne);t1.start();try {System.out.println("waiting for t1 end...");t1.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("all finished.");}}如我们所需要的,父线程会一直等待子线程执行完毕后才会结束。
最后来看一下sleep,在sdk中是这样介绍的
/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param millis the length of time to sleep in milliseconds. * @exception InterruptedException if any thread has interrupted * the current thread. The <i>interrupted status</i> of the * current thread is cleared when this exception is thrown. * @see Object#notify() */ public static native void sleep(long millis) throws InterruptedException; /** * Causes the currently executing thread to sleep (cease execution) * for the specified number of milliseconds plus the specified number * of nanoseconds, subject to the precision and accuracy of system * timers and schedulers. The thread does not lose ownership of any * monitors. * * @param millis the length of time to sleep in milliseconds. * @param nanos 0-999999 additional nanoseconds to sleep. * @exception IllegalArgumentException if the value of millis is * negative or the value of nanos is not in the range * 0-999999. * @exception InterruptedException if any thread has interrupted * the current thread. The <i>interrupted status</i> of the * current thread is cleared when this exception is thrown. * @see Object#notify() */ public static void sleep(long millis, int nanos) throws InterruptedException {if (millis < 0) { throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++;}sleep(millis); }有几个要点:
1、sleep()只是临时停止执行("temporarily cease execution"),并不是
2、"The thread does not lose ownership of any monitors",所以锁什么的都不会释放,相比较wait()方法会释放锁;
3、sleep()也会抛出异常InterruptedException;
这篇介绍了好几个方法都是阻塞方法,也都涉及到了InterruptedException,具体可以看几篇文章
InterruptedException的解读
Java 理论与实践: 处理 InterruptedException
- [java多线程]多线程同步(二)——wait, notify, notifyAll, join以及sleep
- Java多线程——sleep(),wait(),notify(),notifyAll(),join()
- java多线程wait,sleep,notify,notifyAll
- 多线程wait-notify;notifyall.sleep
- Java线程和多线程(二)——对象中的wait,notify以及notifyAll方法
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程sleep(),join(),interrupt(),wait(),notify()
- Java多线程-wait(), notify(), notifyAll()、yield()、sleep()、join()、interrupt()原理及使用
- Java多线程笔记三(线程通信wait/notify/notifyAll/sleep/yield/join)
- java join sleep wait notify notifyAll
- java多线程wait() ,notify() notifyAll()
- 黑马程序员——Java多线程—线程同步—wait、notify、notifyAll
- wait, sleep, notify, notifyall, join
- Java多线程(九)——wait() notify() notifyAll()
- U-BOOT官网链接和linux内核
- codeforces 364B Free Market dp
- get & post
- android之HttpURLConnection
- yii 调用c#写的Webservice时客户端编程方式
- [java多线程]多线程同步(二)——wait, notify, notifyAll, join以及sleep
- Console命令详解,让调试js代码变得更简单
- Java设计模式(一)
- BAT大佬都在焦虑什么?
- UVA - 502 DEL command(字符串)
- 渐增小矩阵
- [java多线程]关于Java和Scala同步机制你不知道的5个真相
- Codeforces Beta Round #85 (Div. 1 Only) C. Petya and Spiders
- uva 572(图)