转自:https://blog.csdn.net/weixin_37695911/article/details/106668435

线程的主要状态及切换:

1.初始-NEW(还未调用start())

2.运行-RUNNABLE(就绪(READY):调用了start(),运行中(RUNNING):获得了时间片这两者统称为运行)

3.阻塞-BLOCKED 因为synchronized没获取到锁的线程就会阻塞在同步队列中(同步队列中放的是尝试获取锁但失败的线程)

4.等待-WAITING(具体情况见下图,会进入等待队列,等待进入同步队列去竞争同步锁)(进入等待队列后,需要notify()唤醒,以进入同步队列)

5.超时等待-TIMED_WAITING

6.销毁-TERMINATED

等待队列(本是Object里的方法,但影响了线程)

  1. 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。

  2. 与等待队列相关的步骤和图

object.notify():将任意一个线程从绿色等待队列移动到红色同步队列

待synchronized同步代码块结束后,会自动释放object锁对象,这时同步队列中的所有线程争抢对象锁

* 1.线程1获取对象A的锁,正在使用对象A。

* 2.线程1调用对象A的wait()方法。

* 3.线程1释放对象A的锁,并马上进入等待队列。

* 4.锁池里面的对象争抢对象A的锁。

* 5.线程5获得对象A的锁,进入synchronized块,使用对象A。

* 6.线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入同步队列。若线程5调用对象A的notify()方法,则唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入同步队列。

* 7.notifyAll()方法所在synchronized结束,线程5释放对象A的锁。

* 8.同步队列的线程争抢对象锁,但线程1什么时候能抢到就不知道了。

注意:等待队列里许许多多的线程都wait()在一个对象上,此时某一线程调用了对象的notify()方法,那唤醒的到底是哪个线程?随机?队列FIFO?or sth else?java文档就简单的写了句:选择是任意性的(The choice is arbitrary and occurs at the discretion of the implementation)。

synchronized只有一个等待队列,一个同步队列

juc.locks包下有多个等待队列,一个同步队列

同步队列是在同步的环境下才有的概念,一个对象(即一个锁)永远只对应一个同步队列。

问题:为什么每个并发包中的同步器会有多个等待队列呢??

不同于synchronized同步队列和等待队列只有一个,AQS的等待队列是有多个,因为AQS可以实现排他锁(ReentrantLock)和非排他锁(ReentrantReadWriteLock——读写锁),读写锁就是一个需要多个等待队列的锁。等待队列(Condition)用来保存被阻塞的线程的。因为读写锁是一对锁,所以需要两个等待队列来分别保存被阻塞的读锁和被阻塞的写锁。

为什么要在竞争锁资源和同步队列中引入等待队列这个概念:

在synchronized中还不好解释,

但是在lock,在condition中,多少个condition就对应多少个等待队列,这样就可以区分出coder希望将当前获得锁的线程放进哪个等待队列中,以达到精准的通知/等待机制。

 
conditionX.await(); //将当前线程lock锁释放,把当前线程放进conditonX对应的等待队列中
conditionX.signalAll(); //把conditonX对应的等待队列中的所有线程唤醒,放进同步队列中

更深的东西涉及到AQS-抽象同步队列!

同步队列状态

1. 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入同步队列。简言之,同步队列里面放的都是想争夺对象锁而失败的线程。

2. 当一个线程1(在等待队列中)被另外一个线程2唤醒时(在线程2里调用Object.notify()),1线程进入同步队列,去争夺对象锁。

3. 同步队列是在同步的环境下才有的概念,一个对象(即一个锁)永远只对应一个同步队列。(此处的一个对象指的就是synchronized(Object object)中的这个object)

感觉是在Object中,多个线程竞争1个锁对象,对应1个等待列队,对应1个同步队列。(正确!)

juc.locks包下,多个线程竞争1个lock锁对象,有多个等待队列,对应1个同步队列

总结:在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,而并发包中的 Lock(更确切地说是同步器)拥有一个同步队列和多个等待队列。


一些我自己的总结:

1.sleep()释放CPU资源,不释放锁:同步线程之间,如果某个线程在同步代码块内调用sleep()方法,会让其他线程干等,因为sleep()方法并不会让当前线程退出代码块。sleep()方法导致了线程暂停指定的时间,让出CPU给其他线程。但是该线程的监控状态依然保持着,当其指定的时间到了又会自动恢复运行状态。

2.wait()释放CPU资源,也释放锁(使自己进入等待队列,依靠notify()以进入同步队列去竞争同步资源)

3.notify()不释放CPU资源,等到自己同步代码块执行完毕后再释放锁(即自己同步代码执行完毕后,再唤醒一个进程离开等待队列,进入同步队列,再让同步队列中的随机一个进程获得锁)

句句都是精华:

1.wait()、notify()是Object类的方法;

2.两者必须在同步代码中使用,调用者就是这个同步代码的锁对象;

3.Object类的锁对象的方法调用会影响锁对象所在的线程;

4.锁对象一旦调用wait(),会让该线程立刻释放锁对象、CPU;

5.锁对象调用notify()后,锁对象所在线程不会立即释放锁对象、CPU,而是等到同步代码执行完毕后,才释放锁对象,并唤醒一个在等待队列的线程,让其进入同步队列,让同步队列中的线程自由竞争,获得CPU以获取调用了notify()方法的线程所释放的锁对象。

关于wait、notify,自己写的一个简单demo,模仿生产者消费者,有很多地方不贴近现实,但是可以跑:

/*
* 写个生产者、消费者,
* 一共两个线程,
* 生产者每5秒生产一次,消费者每5秒消费一次,
* 初始时有一个产品,
* 仓库最多装一个。
* (利用wait()/notify())
* */
public class A20200108 {static boolean flag = true; //初始时有产品static Object lock =new Object();public static void main(String[] args) throws InterruptedException {new Thread(new Wait(),"consumer").start();TimeUnit.SECONDS.sleep(2);new Thread(new Notify(),"producer").start();}static class Wait implements Runnable{@Overridepublic void run() {while (true){try {TimeUnit.SECONDS.sleep(5);synchronized (lock){ //消费者进入仓库while(!flag){ //如果没有产品System.out.println(Thread.currentThread().getName() + "没有产品,进入等待队列");lock.wait();}System.out.println(Thread.currentThread().getName() + "有产品,消费呗");flag = false;}} catch (InterruptedException e) {e.printStackTrace();}}}}static class Notify implements Runnable{@Overridepublic void run() {while (true){try {TimeUnit.SECONDS.sleep(5);synchronized (lock){System.out.println(Thread.currentThread().getName() + "来了,准备生产,唤醒消费者");flag = true;lock.notify();}} catch (InterruptedException e) {e.printStackTrace();}}}}
}

join:由线程调用该方法。(和sleep一样,都是由Thread调用)

join的功能在内部是使用wait()方法来实现的。

作用:让调用join方法的当前线程(注意当前线程的含义,很有可能是指main线程)进入等待队列(相当于隐式调用了wait),当调用join方法的线程执行完毕时(如main线程中的一个A线程),再唤醒当前线程(如主线程)(唤醒:离开等待队列,进入同步队列)(即main线程放弃锁给其他线程后,下一个获得锁的不一定是main,main也需要去公平竞争)(相当于隐式调用了notify())

(即在主线程内,让线程A调用join(),会让主线程隐式调用了wait(),从而进入等待队列,等线程A执行完毕后,再唤醒main线程)

join():

主要作用是同步,它可以使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。

举例:可以在main线程中写

threadA.join();
/*
此代码的意思是阻塞main线程,直到threadA执行完毕(之前threadA已经开始执行了,因为join()方法必须在线程start()方法调用之后调用)(threadA.join()是Thread方法,不像wait()、notify()需要放在同步块里执行)
*/

join()方法必须在线程start()方法调用之后调用才有意义。一个线程都还未开始运行,同步是不具有任何意义的。

即先要threadA.start()后,调用threadA.join()才有意义。

感觉join和wait的区别就在唤醒时机:后者需要在别的线程里手动唤醒(by 锁对象.notify()),前者则只需要threadA.join()执行完毕,调用了这行代码的当前线程就会被唤醒。

哇,看完恍然大悟,原来线程类的join()还能和Object类中的wait()方法扯上关系

wait、notify都是Object类的方法

lock、unlock是ReentrantLock类(实现了Lock接口)的方法

前两者需要借助在synchronized中发挥作用;

后两者必须在lock中,由conditon调用。

线程状态切换之等待队列和同步队列相关推荐

  1. Java基础加强重温_08:线程不安全、线程同步、线程状态、线程状态切换、线程池(Executors类、newFixedThreadPool)、死锁、Lambda表达式、Stream

    摘要 Java基础加强重温_08: 线程安全(线程安全概念.线程不安全案例). 线程同步(同步代码块.同步方法.Lock锁,锁对象). 线程状态(new新建.Runnable可运行.Blocked锁阻 ...

  2. 多线程并发-线程状态切换-后宫狗血剧情版,看完彻底掌握线程状态切换流程

  3. 同步器AQS中的同步队列与等待队列

    在单纯地使用锁,比如ReentrantLock的时候,这个锁组件内部有一个继承同步器AQS的类,实现了其抽象方法,加锁.释放锁也只是涉及到AQS中的同步队列而已,那么等待队列又是什么呢? 当使用Con ...

  4. AbstractQueuedSynchronizer同步队列与Condition等待队列协同机制

    之前对AbstractQueuedSynchronizer(AQS)同步队列与Condition等待队列的功能一直不是很清晰,没太清楚地区分开二者的区别和联系,最近研究了一下分享出来. 1.同步队列和 ...

  5. Java并发知识梳理(上):并发优缺点,线程状态转换,Java内存模型,Synchronized,Volatile,final,并发三特性,Lock与AQS,ReetrandLock

    努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 整个系列文章为Java并发专题,一是自己的兴趣,二是,这部分在实际理解上很有难度,另外在面试过程中也是经常被问到.所以在学习过程中,记 ...

  6. Java多线程-线程状态

    线程状态 6个状态定义:java.lang.Thread.State New: 尚未启动的线程的线程状态. Runnable: 可运行线程的线程状态,等待CPU调度. Blocked: 线程阻塞等待监 ...

  7. 图文详解jvm中的线程状态

    本文使用下面这张图详细介绍JAVA线程的六种状态 JAVA线程的六种状态详解 在java.lang.Thread类中,定义了线程的以下六种状态(同一个时刻线程只能有一种状态)  NEW(新建) 这个状 ...

  8. 线程同步有几种方法_架构师面试必问的多线程状态切换及常用方法

    架构师面试必问的多线程状态切换及常用方法 一.问题背景 Java架构师面试中,多线程状态切换及常用方法几乎是必问的,要掌握创建多线程的方式和方法. 二.创建多线程的几种方式 2.1方式一继承Threa ...

  9. 锁与并发工具包与线程池与LockSupport与Fork/Join框架与并行流串行流与阻塞队列与JPS,jstack命令查看死锁查看线程状态与AQS个人笔记九

    朝闻道,夕死可矣 本文共计 86564字,估计阅读时长1小时 点击进入->Thread源码万字逐行解析 文章目录 本文共计 86564字,估计阅读时长1小时 一锁 二Java中13个原子操作类 ...

最新文章

  1. linux查看信息文件,lsof---Linux查看文件信息的强大工具
  2. MySQL服务器硬件和操作系统调节
  3. maven项目部署到Repository(Nexus)
  4. 如何使用Affinity Designer for mac编辑矢量曲线和形状
  5. Xplanner 项目规划跟踪工具
  6. 闲聊人工智能产品经理(AIPM)—人工智能产品经理的行业理解
  7. 给想进投行的学弟学妹的忠告和建议
  8. 注册免费域名教程(怎样免费注册域名)
  9. 7个必备网站,查征信、查三无产品、查老赖、查犯罪记录!
  10. mcdf项目验证总结-SV
  11. [NodeJS] Mongoose Populate 基本使用
  12. shell 编程三剑客之三:awk 详解
  13. mysql5.6.37 主从同步_Mysql5.6.x版本半同步主从复制的开启方法
  14. 答题软件对于私人来说安全吗?
  15. 最好用的地图匹配框架——基于HMM的Valhalla
  16. 1-2BP神经网络--Keras实现
  17. Excel数据录入快捷操作:
  18. 仿淘宝京东商品规格属性选择的最简单实现
  19. python偏函数应用
  20. Karabiner常用的自带快捷键

热门文章

  1. 软件整合--硬件整合--平台整合
  2. 集成 Tomcat、 Servlet 的生命周期
  3. BitMap-BitSet(JDK1.8)基本使用入门
  4. Junit5集成到SpringBoot工程
  5. MySQL数据库安装Version5.7
  6. Kafka万亿级消息实战干货~持续更新中
  7. Spring注解之@Import用法解析
  8. Java学习前,需要掌握的一些基础知识
  9. 微服务模块综合管理(模块视图管理,自动化热部署,前端资源实时刷新......)
  10. XStream的使用