首页 > 代码库 > wait, notify, sleep, join, interrupt
wait, notify, sleep, join, interrupt
学习多Thread编程,对这几个方法的理解非常重要,然而他们却难以掌握。今天就专门看了看API,下面进行简短的说明:
synchronized
在学习这几个方法前,先要明白synchronized的用法。synchronized就是为对象指定监听,哪个线程在执行synchronized修饰的代码块,就是监听哪个线程。
synchronized的用法:
public class A(){ private static int b; private Object lock=new Object(); private int i;// 为this上锁 public synchronized int add(){ return i++; } public int add2(){// 为this上锁 synchronized(this){ return i++; } } // 为A.class上锁 public static synchronized int add3(){ return b++; }// 为lock对象上锁 public int add4(){ synchronized(lock){ return i++; } }}
使用了synchronized就是为某个对象锁定当前线程,也称为线程拥有了对象的监听(monitor)。
了解了这个之后,再来看看下面的要说的几个方法:
notify()
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 wait methods. 只唤醒等待object的监听的多个线程中的任意一个。通过调用wait方法可以让线程去等待object的监听。 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. 被唤醒的线程不会立即就可以处理操作(被唤醒后,是脱离了阻塞状态,转为可执行状态),而是到当前的线程释放了object的锁。 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(这三种方式就对应了上面说的synchronized的用法):
Only one thread at a time can own an object‘s monitor. 这段话就是说:notify()要在synchronized修饰的语句块中。 |
为啥notify()需要在synchronized块中呢?
notify是唤醒等待的线程的,说明当前线程不是处于等待状态,而是处于监听状态,因此需要在synchronized块中。
wait()
Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed. 让当前线程去等待,直到其他线程来唤醒它,或者等的时间片完毕。 The current thread must own this object‘s monitor. This method causes the current thread (call it T) to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. Object调用了wait,会让当前线程释放掉与object的关联,并被放到等待集合中(也就是让当前线程释放掉CPU时间片,失去与object的锁定,处于阻塞状态)。 Thread T becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:
有四种方式可以让线程脱离等待: ·其他线程调用执行时调用了notify(),并且现象处于轮流的被唤醒、被关闭状态(其实就是阻塞状态)。 ·其他线程调用了notifyAll() ·别的线程打断了线程T。 ·指定的等待时间完毕。 总结一下就是: 线程脱离阻塞状态的三种方式: 被唤醒、等待时间完毕:这两种会进入可执行状态。 被打断(会抛异常的):这种会让线程die。
如果不指定等待时间(或者指定等待时间为0),那就等着别的线程唤醒你吧。
The thread T 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 wait method was invoked. Thread T then returns from the invocation of the wait method. Thus, on return from the wait method, the synchronization state of the object and of thread T is exactly as it was when the wait method was invoked. 线程T被唤醒后,就从等待集合中被移除(脱离阻塞状态)。如果唤醒线程,线程T一旦获取到了Object的控制权(被object监听),线程T就会还原等待前的状态,然后接着原来的位置开始执行。
If the current thread is interrupted by any thread before or while it is waiting, then an InterruptedException is thrown. This exception is not thrown until the lock status of this object has been restored as described above. 一旦线程(处于等待状态或者等待前)一旦被任意一个线程打断(包括它自己,打断的方式是调用interrupt()方法),就会在线程获取到object控制权,并还原等待前的状态,要接着执行时,抛出InterruptedException。 This method should only be called by a thread that is the owner of this object‘s monitor. See the Wait要在synchronized中使用。 |
也就是说wait的使用是这样的:
synchronized(this){
//xxxxx
this.wait();
}
是这样的:
synchronized void method (){
wait();
}
是这样的:
static synchronized void method(){
clazz.wait();// clazz代表一个Class对象
}
是这样的:
synchronized(lock){
lock.wait();
}
也就是说,对谁使用了synchronized,就用谁的wait方法。
sleep()
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. 让当前线程休息一会,但对象仍旧拥有object的监听,没有与Object切断联系。没有切断联系,别的线程就不能拿到锁定执行。
|
sleep与wait的都能让当前线程先不执行。但wait会 让当前线程失去监听,sleep不会。
join()
join不好理解,
Waits at most |
根据上面的说明,很容易理解为:等待当前线程死了,至少得等待指定的毫秒数。如果指定时间为0,那就是一种等下去。
package com.fjn.other.threadTest; public class ThreadTest{ private final long _1S=1000L;
public void testJoin(){ Thread aThread=new Thread(new Runnable() {
public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(_1S); System.out.println(Thread.currentThread().getName()+":\t"+i); } catch (InterruptedException e) { e.printStackTrace(); } } } },"Thread-A"); aThread.start();
new Thread(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(_1S*2); System.out.println(Thread.currentThread().getName()+":\t"+i); } catch (InterruptedException e) { e.printStackTrace(); } }
} },"Thread-B").start(); }
public static void main(String[] args) { new ThreadTest().testJoin(); } }
|
上面的代码执行是这样的(只取了一部分结果):
Thread-A: 0 Thread-A: 1 Thread-B: 0 Thread-A: 2 Thread-A: 3 Thread-B: 1 Thread-A: 4 Thread-A: 5 Thread-B: 2 Thread-A: 6 Thread-A: 7 Thread-B: 3 Thread-A: 8 |
然后加上join:
package com.fjn.other.threadTest; public class ThreadTest{ private final long _1S=1000L;
public void testJoin(){ Thread aThread=new Thread(new Runnable() {
public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(_1S); System.out.println(Thread.currentThread().getName()+":\t"+i); } catch (InterruptedException e) { e.printStackTrace(); } } } },"Thread-A"); aThread.start(); try { aThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } new Thread(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(_1S*2); System.out.println(Thread.currentThread().getName()+":\t"+i); } catch (InterruptedException e) { e.printStackTrace(); } }
} },"Thread-B").start(); }
public static void main(String[] args) { new ThreadTest().testJoin(); } }
|
我以为结果应该是:aThread等死了。线程B正常执行。
但结果是这样的:
Thread-A: 0 Thread-A: 1 Thread-A: 2 Thread-A: 3 Thread-A: 4 Thread-A: 5 Thread-A: 6 Thread-A: 7 Thread-A: 8 Thread-A: 9 Thread-B: 0 Thread-B: 1 Thread-B: 2 Thread-B: 3 Thread-B: 4 Thread-B: 5 Thread-B: 6 Thread-B: 7 Thread-B: 8 Thread-B: 9 |
从结果来看是,线程A正常执行完毕后,才执行线程B。
这是为什么呢???
不理解,网上说的也不明确,看源码:
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; } } } |
指定时间为0:就是执行:while(isAlive()){wait(0)}
意思是说:指定时间为0,就直接执行wait(0)。上面说wait()是让当前线程等待呀,如果wait(0),就等着别的线程notify呀。上面的代码中,是不可能有别的线程notify的呀。那也就是说Thread-A是永远不能被唤醒的呀,不能被唤醒,就不能再获得对象锁呀,当然Thread-A就不能继续往下走呀。分析的结果是这样的,实际的结果却是那样的,为什么呢??????
在重新看源码:
上面说了:对谁使用了synchronized,就用谁的wait()。
也就是说,源码中是对aThread对象使用了synchronized,也就是说让当前线程等待,等待继续获取到aThread对象的监听。也就是说:不是让aThread等待,而是让当前线程等aThread。等aThread死了,就是等aThread执行完毕而死或者被打断而死。
那上面的代码的意思就是,让main线程等待,等到aThread执行完毕,然后开始执行main。
所以上面的代码的执行过程应该是:
main执行过程中,
1、创建aThread对象
2、启动aThread,aThread开始执行。
3、main执行join(),让自己等待,等aThread执行完毕
4、aThread执行完毕,main继续执行
5、创建B线程并开始执行
在看join()的说明:
Waits at most |
等待指定时间,等待this.join()中的this代表的线程去死。如果时间为0,就一直等到this代表的线程死亡。如果时间不为0,就等待指定时间就行了,如果this线程没有死也不等了。
interrupt()
Interrupts this thread. Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown. If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException. If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread‘s interrupt status will be set, and the thread will receive a ClosedByInterruptException. If this thread is blocked in a Selector then the thread‘s interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector‘s wakeup method were invoked. If none of the previous conditions hold then this thread‘s interrupt status will be set. Interrupting a thread that is not alive need not have any effect.
|