关于 java线程的 锁池、等待池(二)

2023-11-03 15:58
文章标签 java 线程 等待 锁池

本文主要是介绍关于 java线程的 锁池、等待池(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

围绕着:「等待池中被 "唤醒"notifyAll() 的线程一定会进入锁池吗?」

接着上篇转载的博客:https://blog.csdn.net/ScorpC/article/details/90296146

自己又继续深入研究了这个问题,查阅了java虚拟机经典书籍《Inside the Java Virtual Machine(第2版)》,瞬间懂!

不仅感叹,经典就是经典,能成为经典一定是有原因的!

也告诫自己,尤其是基础的东西,遇到问题,最好优先自己亲自查阅书籍、阅读源码、官方开发文档等

共勉!

以下内容总结自此书的:第20章 线程同步

 

 

可以在语言级支持多线程是java语言的一大优势,这种支持主要集中在同步上,或调解多个线程的活动和共享数据。Java所使用的同步机制是监视器。本章讲述了监视器,展示了java虚拟机使用它们的方式,还描述了指令集在数据的锁定和解锁方面是如何支持监视器的。

1. java中的监视器支持两种线程:互斥和协作

   java虚拟机通过对象锁来实现互斥;

   协作则是通过Object类的wait()和notify()方法来实现;

2. java虚拟机所使用的这种监视器被称作“等待并唤醒”监视器(有时也被称作“发信号并继续”监视器);

 

在这种监视器中,一个已经持有监视器的线程,可以通过执行一个等待命令,暂停自身的执行。当线程执行了等待命令后,它就会释放监视器,并进入一个等待区,这个线程会在那里一直持续暂停状态,直到一段时间后,这个监视器中的其他线程执行了唤醒命令。当一个线程执行了唤醒命令之后,它会继续持有监视器,直到它主动释放监视器,如执行了一个等待命令或者执行完监视区域。当执行唤醒的线程释放了监视器后等待线程苏醒,并重新获得监视器。

 

java虚拟机中的监视器模型如上图所示,将监视器分成了三个区域:

         中间的大方框包括一个单独的线程,是监视器的持有者;

         左边的小方框是入口;

         右边是另一个小方框是等待区;

         活动线程用深灰色圆画出;

         暂停的线程用浅灰色画出;

 

3. 上图也显示了线程与监视器交互所必须“通过”的几道门;

 

当一个线程到达监视区域的开始出时,它会通过最左边的1号门进入监视器,发现自己身处在那个叫入口区的方框中;

 

如果该没有任何线程正持有监视器,也没有其他线程正在入口区中等待,这个线程就会立刻通过下一道门:2号门,并持有监视器;

作为这个监视器的持有者,它将继续执行监视区域中的代码;

 

或者也可能出现另一种情况,已经有另一个线程正持有监视器,这个新到达的线程就必须在入口区域等待,很可能那里已经有一些线程在等待了,这个线程会被阻塞,所以不能执行监视区域中的代码;

 

如果一个监视器的持有者在它释放监视器前没有执行唤醒命令(同时在此之前也没有任何等待线程被唤醒并等待苏醒),那么位于入口区中的那些线程将会竞争获得监视器。如果上一个持有者执行了唤醒命令,入口区中的线程就不得不与一个或多个等待区中的线程来竞争。而如果是等待区中的某个线程赢了,它会通过4号门退出等待区并重新获得监视器。注意,一个线程只有通过3号门和4号门才能进入或退出等待区。一个线程只有在它正持有监视器时才能执行等待命令,而且它只能通过再次成为监视器的持有者才能离开等待区;

 

在java虚拟机中,线程在执行等待命令时可以随意指定一个暂停时间。如果一个线程指定了暂停时间,而且在暂停时间截止之前没有其他线程执行唤醒命令。这个等待线程会从虚拟机得到一个自动唤醒的命令。也就是说,在暂停时间到了以后,即使没有来自其他线程的唤醒命令,它也会自动苏醒。

 

Java虚拟机提供了两种唤醒命令:”notify”和”notify all“。notify命令随意从等待区中选择一个线程并将其标志为 "可能苏醒" ,而notify all 命令会将等待区中的所有线程都标志成 "可能苏醒"

 

Java虚拟机如何从等待区以及入口区选择下一个线程来执行,在很大程度上取决于java虚拟机的设计者;

 

总之,实现可以任意自由选择哪一个线程;

 

程序员必须不依赖任何特定的有关优先级的算法或者安排,至少在编写平台无关的java程序时应该这样;

 

比如说,因为不知道notify命令将会导致等待区中的哪一个线程苏醒,只有当绝对确认只会有一个线程在等待区中挂起的时候,才应该使用notify(相对notify all)而言。只要存在同时有多个线程在等待区中被挂起的可能性,就应该使用notify all。

 

4.对象锁

在前面的章节中提到过,java虚拟机的一些运行时数据区会被所有的线程共享,其他的数据是各个线程私有的;

 

因为方法区是被所有线程共享的,java程序需要为两种多线程访问数据进行协调:

        保存在堆中的实例变量;

        保存在方法区中的类变量;

程序不需要协调保存在java栈中的局部变量,因为java栈中的数据是属于拥有该栈的线程私有的;

 

在java虚拟机中,每个对象和类在逻辑上都是和一个监视器相关联的。对于对象来说,相关联的监视器保护对象的实例变量。对于类来说,监视器保护类的类变量。如果一个对象没有实例变量,或者一个类没有类变量,相关联的监视器就什么都不监视。

 

类锁实际上用对象锁实现:java虚拟机在加载一个class文件的时候,它会创建一个java.lang.Class类的实例。当锁住一个类的时候,实际上锁住的是那个类的Class对象。

 

一个线程可以允许多次对同一个对象上锁。对于每一个对象来说,java虚拟机维护一个计数器,记录对象被加了多少次锁。没有被锁的对象的计数器是0.但一个线程第一次获得锁的时候,计数器跳到1. 线程每加锁一次,计数器就加1。(只有已经拥有了这个对象的锁的线程才能对该对象再次加锁,在它释放锁之前,其他的线程不能对这个对象加锁)每当线程释放锁一次,计数器就减1.当计数器跳到0的时候,锁就完全释放了,其他的线程才可以使用它。

 

注意:java程序员不需要自己动手加锁,对象锁是在java虚拟机内部使用的。在java程序中,你只需要编写同步语句或者同步方法就可以标志一个监视区域。当java虚拟机运行你的程序的时候,每一次进入一个监视区域的时候,它每次都会自动锁上对象或者类。

 

 

这篇关于关于 java线程的 锁池、等待池(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/339269

相关文章

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H