最权威的资料参考官方 Thread源码里的内部枚举 java.lang.Thread$State
的说明。其描述仅仅缺失了对于runnable状态更加细致的描述,runnable应该拆分成ready和running。
除了官方的资料,下面也会综合若干博客的资料图。有些状态的名称可能比较混乱,有多种叫法,甚至出现含义的差异,需要读者自行理解。
线程状态转换注意:官方的runnable状态,细划分为就绪
(ready)和运行中
(running)。有些资料也把就绪
叫做可运行
,即直接把runnable当成就绪,官方的runnable其实是ready和running的统称
- 线程被实例化后进入
new
- new状态的线程start()后进入
ready
,即可以被CPU挑选的状态 - ready状态的线程被CPU挑选中,进入
running
- running若遇到synchronized,进入
blocked
- running若wait(),则放弃锁,进入
waiting
- running若sleep(timeout),进入
timed_waiting
(带有超时的等待状态) - running若yield()则让出时间片 或 时间片用完,进入ready(yield可能刚让出立即又被挑选)
- running若运行完毕,进入
terminated
- blocked的线程,在拿到锁后,进入ready(并非拿到锁立即可以运行)
- waiting和timed_waiting的线程,在其他线程通知后进入ready(并非被通知后立即可运行)
上述的版本是简单的版本
- yield()让出时间片,只会让给相同优先级的其他线程
- sleep(timeout)后,其他任意优先级的线程都有可能获得机会
- 为什么wait()/notify()/notifyAll()方法是Object的而不是线程的,因为锁对象可以是任意的Object(包括java.lang.Thread对象),只要线程之间竞争的是同一个Object,即同一个锁,那么使用这个锁锁起来的代码,就能保证同一时间只有一个获锁线程能进入同步块。另外不同线程之间,就是使用同一个锁对象的wait()/notify()/notifyAll()进行通信的,线程之间的通信,总要有个共同的东西,这个共同的东西就是这个锁。
补充图1:
补充图2:
(无)
API有些是Object的接口,有些是Thread的,有些是LockSupport的
1、wait(timeout)/wait()public final native void wait(long timeout) throws InterruptedException
public final void wait() throws InterruptedException,转调有参版
Object的方法,这个Object是所谓的锁对象,其实真正的锁对象应该是Object’s monitor才对吧? 所以这里的监视器的描述就是一般指的 “锁” 或 “锁对象”
-
调用wait()等价于wait(0),后者是native方法
-
调用wait(timeout)表示当前线程进入等待,并释放所持有的监视器,直到以下四种情况之一发生:
- 别的线程通知notify,并被随机挑选为唯一被唤醒的线程
- 别的线程通知notifyAll
- 别的线程interrupt该线程
- 如果timeout>0,指定的时间到了则被唤醒;如果timeout=0则此条规则无效
该线程被唤醒后,跟其他线程无差别公平地竞争锁,等到获取了锁再从断点继续运行
-
当前线程必须拥有锁(即该Object的监视器),即必须在synchronized方法或代码块里才能用wait方法
-
要注意使用while来进行条件判断,而不是if,因为线程可能会打断(interrupts)和假醒(spurious)
synchronized (obj) {
while ()
obj.wait(timeout);
... // Perform action appropriate to condition
}
/**
* 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.
*
* 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. Thread T
* becomes disabled for thread scheduling purposes and lies dormant
* until one of four things happens:
*
* - Some other thread invokes the {@code notify} method for this
* object and thread T happens to be arbitrarily chosen as
* the thread to be awakened.
*
- Some other thread invokes the {@code notifyAll} method for this
* object.
*
- Some other thread {@linkplain Thread#interrupt() interrupts}
* thread T.
*
- The specified amount of real time has elapsed, more or less. If
* {@code timeout} is zero, however, then real time is not taken into
* consideration and the thread simply waits until notified.
*
* 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 {@code wait}
* method was invoked. Thread T then returns from the
* invocation of the {@code wait} method. Thus, on return from the
* {@code wait} method, the synchronization state of the object and of
* thread {@code T} is exactly as it was when the {@code wait} method
* was invoked.
*
* A thread can also wake up without being notified, interrupted, or
* timing out, a so-called spurious wakeup. 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:
*
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
*
* (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).
*
* If the current thread is {@linkplain java.lang.Thread#interrupt()
* interrupted} by any thread before or while it is waiting, then an
* {@code InterruptedException} is thrown. This exception is not
* thrown until the lock status of this object has been restored as
* described above.
*
*
* Note that the {@code wait} 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.
*
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} 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.
* @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 interrupted
* status 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;
2、notify()
public final native void notify()
- 通知该Object上等待的线程,如果有多个,随意一个会被通知
- 当前线程必须有该Object的锁,即notify必须写在synchronized的方法或代码块里
- 调用notify后,当前线程并不是立即释放锁的,要等到同步方法或同步代码块执行完
- 被通知的线程必须等到锁后才能恢复运行,恢复到调用wait()的时刻
- 被通知的线程会参与到锁的竞争中,没有优势也没有劣势,公平竞争
/**
* 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} methods.
*
* 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.
*
* 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:
*
* - By executing a synchronized instance method of that object.
*
- By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
*
- For objects of type {@code Class,} by executing a
* synchronized static method of that class.
*
*
* Only one thread at a time can own an object's monitor.
*
* @throws 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();
3、notifyAll()
public final native void notifyAll()
- 跟notify完全一样,唯一的不同是它会通知所有Object上等待的线程
public final synchronized void join(long millis) throws InterruptedException
public final void join() throws InterruptedException,转调有参版
-
当前线程
等待目标线程
一定时间,或等到目标线程运行结束(die)再继续执行,两个条件以先达到的为准例如代码
t.join()
,意思是当前线程,就是执行t.join()
代码的那个线程(假设是M)必须等到t
线程执行完毕,t.join()
后面的代码才能继续被M执行。join的意思就是将
t
join到M
里,只要t没完成,M就卡在t.join()
这里,这就是join的含义。 -
上述说的join(),等价于join(0)
-
join()通常用在main线程里,所有其他线程执行完毕再结束main线程
-
实现:其内部使用wait来实现,非native方法。
相当简单,主线程是M,执行t.join(0),join方法是同步的,M获得锁(t是锁对象),isAlive()判断的是t是否还活着,如果还未结束,执行wait(0),于是M就释放锁,进入到wait队列。等t运行结束的时候执行t的notifyAll(在native代码里,参考这里),这样M线程恢复了执行。这样就实现了M等t执行完再执行。
思考:
其实join(timeout)真的很像sleep(timeout),不同点是join(timeout)无需一定等到timeout,在目标线程结束后即可终止等待。而join()的话可以实现无限等,而sleep是没对应的。另外从机制上两者是不同的
写例子帮助理解join要实现其他线程都执行完,main线程才最后执行(做收尾工作),有三种方式
- 使用join()
- 使用CountDownLatch
- 使用yield()
使用join()
// 结论:main线程不等待t线程完成,自己先打印了结果
// 将代码 t.join()打开后,当前线程(main线程)
// 会等待目标线程(t,就是join()的方法那个)执行完毕才继续
// 所谓 "执行完毕",就是 t 线程的run方法执行完线程死掉了
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i 1) {
Thread.yield();
}
使用CountDownLatch
// PS:CountDownLatch 允许多线程操作
public class JoinTest {
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?