原来还能这么看Java线程的状态及转换( 五 )

结果:
线程1等待获取 对象A的锁...线程1获取了 对象A的锁线程2等待获取 对象A的锁...线程1开始运行wait()方法进行等待 , 进入到等待队列......线程2获取了 对象A的锁线程2将要运行notify()方法进行唤醒线程1线程1等待结束需要注意的是 , wait/notify/notifyAll 只能在synchronized修饰的方法、块中使用 ,  notify 是只随机唤醒一个线程 , 而 notifyAll 是唤醒所有等待队列中的线程
joinThread类中的join方法的主要作用能让线程之间的并行执行变为串行执行 , 当前线程等该加入该线程后面 , 等待该线程终止
public static void main(String[] args) {Thread thread = new Thread();thread.start();thread.join();...}上面一个例子表示 , 程序在main主线程中调用thread线程的join方法,意味着main线程放弃CPU时间片(主线程会变成 WAITING 状态) , 并返回thread线程 , 继续执行直到线程thread执行完毕 , 换句话说在主线程执行过程中 , 插入thread线程 , 还得等thread线程执行完后 , 才轮到主线程继续执行
如果查看JDKthread.join()底层实现 , 会发现其实内部封装了wait(),notifyAll()
park/unparkLockSupport.park() 挂起当前线程;LockSupport.unpark(暂停线程对象) 恢复某个线程
package com.zj.ideaprojects.demo.test3;import java.util.concurrent.Executors;import java.util.concurrent.locks.LockSupport;public class ThreadLockSupportTest {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {System.out.println("start.....");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("park....");LockSupport.park();System.out.println("resume.....");});thread.start();Thread.sleep(3000);System.out.println("unpark....");LockSupport.unpark(thread);}}结果:
start.....park....unpark....resume.....当程序调用LockSupport.park() , 会让当前线程A的线程状态会从 RUNNABLE 变成 WAITING , 然后main主线程调用LockSupport.unpark(thread) , 让指定的线程即线程A,从 WAITING 回到 RUNNABLE。我们可以发现 park/unpark和wait/notify/notifyAll很像 , 但是他们有以下的区别:

  1. wait , notify 和 notifyAll 必须事先获取对象锁 , 而 unpark 不必
  2. park、unpark 可以先 unpark ,而 wait、notify 不能先 notify , 必须先wait
  3. unpark 可以精准唤醒某一个确定的线程 。而 notify 只能随机唤醒一个等待线程 , notifyAll 是唤醒所以等待线程 , 就不那么精确
超时等待状态超时等待状态(TIMED_WAITING) , 也叫限期等待 , 可以在指定的时间后自行返回而不是像 WAITING 那样一直等待 。
这部分比较简单 , 它和线程等待状态(WAITING)状态 非常相似 , 区别就是方法的参数舒服传入限制时间 , 在 Timed Waiting状态时会等待超时 , 之后由系统唤醒 , 或者也可以提前被通知唤醒如 notify
相关方法主要有:
1. Object.wait(long)2. Thread.join(long) 3. LockSupport.parkNanos(long)4. LockSupport.parkUntil(long)5. Thread.sleep(long)需要注意的是Thread.sleep(long),当线程执行sleep方法时 , 不会释放当前的锁(如果当前线程进入了同步锁) , 也不会让出CPU 。sleep(long)可以用指定时间使它自动唤醒过来 , 如果时间不到只能调用interrupt方法强行打断 。
参考资料:
https://hg.openjdk.java.net/jdk8u 《并发编程的艺术》 https://www.jianshu.com/p/216a41352fd8


推荐阅读