您当前的位置: 首页 >  Java

石头wang

暂无认证

  • 4浏览

    0关注

    295博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java线程状态的转换和API

石头wang 发布时间:2019-04-26 15:21:33 ,浏览量:4

Java线程状态的转换和API 线程状态

最权威的资料参考官方 Thread源码里的内部枚举 java.lang.Thread$State 的说明。其描述仅仅缺失了对于runnable状态更加细致的描述,runnable应该拆分成ready和running。

除了官方的资料,下面也会综合若干博客的资料图。有些状态的名称可能比较混乱,有多种叫法,甚至出现含义的差异,需要读者自行理解。

线程状态转换

image

注意:官方的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: iamge

补充图2: iamge

线程API 概述

(无)

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上等待的线程
4、join(timeout)/join()

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             
关注
打赏
1663722529
查看更多评论
0.0441s