看《Java特种兵》的时候发现,Thread.join可使线程进入WAITING状态,再结合姊妹篇线程的状态咱们能够了解到,有两个类状态很是接近:WAITING(TIMED_WAITING) 与 BLOCKED,这二者都会让线程看上去“阻塞”在某处了。java
何时线程会进入WAITING(无限期等待)的状态中呢?经常使用的有两个,分别是①Object.wait without timeout,②Thread.join without timeout【另外还有③LockSupport的park方法,④Conditon的await方法】;TIMED_WAITING除了①Object.wait with timeout、②Thread.join with timeout,还须要添加一条③Thread.sleep方法【另外还有④LockSupport的parkNanos方法,带有时间】。安全
在进入WAITING状态前,线程会将持有的锁先释放掉。WAITING状态中的线程须要被其余线程对同一个对象调用notify()或notifyAll()方法才能唤醒。被notifyAll()唤醒后的线程,拿不到锁的线程将进入BLOCKED状态,直到它们拿到锁为止。简而言之,WAITING类状态中的线程和BLOCKED状态中的线程的区别在于:WAITING状态的线程须要被其余线程唤醒;BLOCKED中的线程,须要等待其余线程释放锁,此处的锁特指synchronized块。app
见下图ide
Join为何会使线程进入WAITING(TIMED_WAITING) 状态中呢?咱们看一下底层代码oop
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; } } }
从十二、20行中能够看出,join方法底层调用的就是wait方法。测试
一样会使线程进入TIMED_WAITING状态中的sleep和wait方法,它们之间有什么区别呢?ui
wait()是非静态方法this
public final void wait() throws InterruptedException { //...}
sleep()是静态方法线程
public static void sleep(long millis, int nanos) throws InterruptedException { //... }
synchronized(obj) { while (!condition) { obj.wait(); } }
这里引用一段《Effective Java》
始终应该使用wait循环模式来调用wait方法;永远不要在循环以外调用wait方法。循环会在等待以前和以后测试条件。
在等待以前测试条件,当条件已经成立时就跳过等待,这对于确保活性(liveness)是必要的。若是条件已经成立,而且在线程等待以前,notify (或者notifyAll)方法已经被调用, 则没法保证该线程将会从等待中苏醒过来。
在等待以后测试条件,若是条件不成立的话继续等待,这对于确保安全性(safety)是必要的。当条件不成立的时候,若是线程继续执行,则可能会破坏被锁保护的约束关系。当条件不成立时,有下面一些理由可以使一个线程苏醒过来:
- 另外一个线程可能已经获得了锁,而且从一个线程调用notify那一刻起,到等待线程苏醒过来的这段时间中,获得锁的线程已经改变了受保护的状态。 - 条件并不成立,可是另外一个线程可能意外地或恶意地调用了 notify。在公有可访问的对象上等待,这些类实际上把本身暴露在了这种危险的境地中。公有可访问对象的同步方法中包含的wait都会出现这样知问题。 - 通知线程(notifying thread)在唤醒等待线程时可能会过分“大方”。例如,即便只有某一些等待线程的条件已经被知足,可是通知线程可能仍然调用notifyAll。 - 在没有通知的状况下,等待线程也可能(但不多)会苏醒过来。这被称为“伪唤醒 (spurious wakeup)”
咱们针对【跳过等待】和【继续等待】举个形象的例子:code
针对【前置判断,跳过等待】:若是是两个狙击手,在同时等待一我的(锁),判断条件是这我的还活着。若是没有前置的判断,在等待前不校验这我的是否活着,那么当狙击手甲杀死目标并通知狙击手乙以后,乙才进入等待状态,那么乙将会死等一个已经被杀死的目标,乙将失去活性(liveness)。
针对【后置判断,继续等待】:仍是两个狙击手,若是他们被唤醒后,没有后置校验,那么将致使好笑的错误,好比狙击手甲已经将目标杀死了,狙击手乙被唤醒后,没有再校验条件,直接开枪杀人,将会杀死目标两次。若是是幂等的还则罢了,不幂等的将致使错误。
综上所述,wait先后都要校验,而最好的办法就是循环。
从上文中咱们已经知道了,join可使线程处于WAITING状态,那问题来了,是子线程处于WAITING状态仍是父线程处于WAITING状态?咱们作个小试验:
public class TestJoin { public static void main(String[] args) throws InterruptedException { Thread.currentThread().setName("TestJoin main...."); Thread joinThread = new Thread(new Runnable() { @Override public void run() { for (; ; ) { } } }, "join thread"); joinThread.start(); joinThread.join(); } }
按照在线程的状态中提供的方法,咱们能够获得:
子线程即join的线程依旧是RUNNABLE状态
"join thread" #10 prio=5 os_prio=31 tid=0x00007fca1b801000 nid=0x5503 runnable [0x0000700001725000]
java.lang.Thread.State: RUNNABLE
at com.meituan.java8.thread.TestJoin$1.run(TestJoin.java:13) at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
父线程(在此例中是主线程)为WAITING状态
"TestJoin main...." #1 prio=5 os_prio=31 tid=0x00007fca1c003000 nid=0x1903 in Object.wait() [0x00007000003ec000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076ad94fa0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1252) - locked <0x000000076ad94fa0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1326) at com.meituan.java8.thread.TestJoin.main(TestJoin.java:20) Locked ownable synchronizers: - None
咱们对代码作稍稍改动,能够验证sleep后的线程在什么状态
Thread joinThread = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.MINUTES.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } }, "sleeping thread");
"sleeping thread" #10 prio=5 os_prio=31 tid=0x00007f92620bc000 nid=0x5503 waiting on condition [0x00007000077b7000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.meituan.java8.thread.TestJoin$1.run(TestJoin.java:16) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - None
https://javaconceptoftheday.c...
A thread enters into WAITING state when it calls wait() or join() method on an object. Before entering into WAITING state, thread releases the lock of the object it holds. It will remain in WAITING state until any other thread calls either notify() or notifyAll() on the same object.
Once the other thread calls notify() or notifyAll() on the same object, one or all the threads which are WAITING for lock of that object will be notified. All the notified threads will not get the object lock immediately. They will get the object lock on a priority basis once the current thread releases the lock. Until that they will be in BLOCKED state.
In simple terms, a thread will be in WAITING state if it is waiting for notification from other threads. A thread will be in BLOCKED state if it is waiting for other thread to release the lock it wants.
https://stackoverflow.com/que...
The difference is relatively simple.
In the BLOCKED state, a thread is about to enter a synchronized block, but there is another thread currently running inside a synchronized block on the same object. The first thread must then wait for the second thread to exit its block.
In the WAITING state, a thread is waiting for a signal from another thread. This happens typically by calling Object.wait(), or Thread.join(). The thread will then remain in this state until another thread calls Object.notify(), or dies.
https://stackoverflow.com/a/3...
wait()
wait() method releases the lock.
wait() is the method of Object class.
wait() is the non-static method - public final void wait() throws InterruptedException { //...}
wait() should be notified by notify() or notifyAll() methods.
wait() method needs to be called from a loop in order to deal with false alarm.
wait() method must be called from synchronized context (i.e. synchronized method or block), otherwise it will throw IllegalMonitorStateException
sleep()
sleep() method doesn't release the lock.
sleep() is the method of java.lang.Thread class.
sleep() is the static method - public static void sleep(long millis, int nanos) throws InterruptedException { //... }
after the specified amount of time, sleep() is completed.
sleep() better not to call from loop(i.e. see code below).
sleep() may be called from anywhere. there is no specific requirement.
4.《Effective Java》第10章