【Java】线程的同步——synchronized、ReentrantLock

        对同一个线程,能否在获取到锁以后继续获取同一个锁?

        答案是肯定可以获取同一个锁。因为JVM 允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁。       

一、synchronized同步锁

        在 Java中synchronized 同步锁是一种可重入的锁。

        什么是Synchronized同步锁?

        Synchronized 同步锁,简单来说,使用 Synchronized 关键字将一段代码逻辑,用一把锁给锁起来,只有获得了这把锁的线程才访问。并且同一时刻,只有一个线程能持有这把锁,这样就保证了同一时刻只有一个线程能执行被锁住的代码,从而确保代码的线程安全。
        

1. 语法

(1)synchronized代码块:自定义对象,作为锁。

(2)synchronized修饰普通方法:使用this关键字获取当前对象,作为锁。

(3)synchronized修饰静态方法:使用当前类的Class对象,作为锁。

2. synchronized实现原理

(1)synchronized关键字通过monitor enter/monitor exit两个指令实现。

(2)通过Monitor监视器机制实现线程的同步:

                Owner 线程拥有者

                EntryList 线程阻塞区

                WaitSet 线程等待区

3. synchronized锁升级(锁膨胀)

        在JDK1.6之前,synchronized性能开销较大;在JDK1.6之后,对synchronized进行了优化,它会自动根据程序的执行情况,自动进行锁的升级:偏向锁->轻量级锁->重量级锁。

        偏向锁(偏斜锁):只有一个线程访问时,使用偏向锁,通过Owner记录线程ID(ThreadID)实现。

        轻量级锁:出现多个线程访问时(没有并发),使用轻量级锁,通过CAS实现。

        重量级锁:出现多个线程并发访问时,使用重量级锁。由于重量级锁,使用操作系统的互斥锁实现。(使用互斥锁,从“用户态”切换至“内核态”,带来性能开销,所以性能相对较差)

4. synchronized线程安全的案例

    可变字符串的线程安全

        (1)StringBuffer : 线程安全(在改变字符串内容的方法上使用synchronized同步锁),性能较差

        (2)StringBuilder:线程不安全,性能较好

    集合类的线程安全(使用synchronized关键字实现线程安全)

        (1)List接口的线程安全实现类:Vector、Stack

        (2)Map接口的线程安全实现类:Hashtable

5. synchronized 关键字的补充

        当一个线程访问对象的一个 synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非 synchronized(this)同步代码块
        在没有加锁的情况下,所有的线程都可以自由地访问对象中的代码,而synchronized关键字只是限制了线程对于已经加锁的同步代码块的访问,并不会对其他代码做限制。所以,同步代码块应该越短小越好。
        父类中 synchronized 修饰的方法,如果子类没有重写,则该方法仍然是线程安全性;如果子类重写,并且没有使用 synchronized 修饰,则该方法不是线程安全的。
        在定义接口方法时,不能使用 synchronized 关键字。
        构造方法不能使用 synchronized 关键字,但可以使用 synchronized 代码块来进行同步
        离开 synchronized 代码块后,该线程所持有的锁,会自动释放

二、ReentrantLock锁

        synchronized 关键字虽然已经实现可重入锁,但由于获取时必须一直等待,没有额外的尝试机制。所以,在 java.util.concurrent.locks 包提供的 ReentrantLock用于替代 synchronized。顾名思义, ReentrantLock 也是可重入锁,它和 synchronized 一样,一个线程可以多次获取同一个锁。

        ReentrantLock是核心类库提供的锁实现类,实现了Lock接口,通过lock()方法加锁unlock()方法释放锁

        支持公平锁非公平锁,内部通过AQS机制实现。

        通过trylock()方法支持获取锁的尝试机制。使用 ReentrantLock比直接使用 synchronized 更安全,线程在 tryLock()失败的时候不会导致死锁

        ReentrantLock总共有三个内部类:Sync、NonfairSync、FairSync。

        NonfairSync 类继承了 Sync 类,表示采用非公平策略获取锁:每一次都尝试获取锁,不会按照公平等待的原则进行等待,不会让等待时间最久的线程获得锁。
       
        FairSync 类也继承了 Sync 类,表示采用公平策略获取锁:当资源空闲时,它总是会先判断sync 队列是否有等待时间更长的线程,如果存在,则将当前线程加入到等待队列的尾部,实现了公平获取原则。

        ReentrantLock 构造函数:默认是采用的非公平策略获取锁。

        ReentrantLock实现线程安全的案例:CopyOnWriteArrayList、ArrayBlockingQueue

public class Main {public static void main(String[] args) {// 公共的锁对象final ReentrantLock lock = new ReentrantLock();// 通过锁对象,创建用于线程之间通信的Condition对象final Condition condition = lock.newCondition();Thread t1 = new Thread(new Number(lock, condition));Thread t2 = new Thread(new Character(lock, condition));t1.start();t2.start();}
}class Number implements Runnable {private ReentrantLock lock;private Condition condition;public Number(ReentrantLock lock, Condition condition) {this.lock = lock;this.condition = condition;}@Overridepublic void run() {final ReentrantLock lock = this.lock;// 加锁lock.lock();try {for (int i = 1; i < 53; i++) {if (i % 2 == 1) {System.out.print(" ");}System.out.print(i);if (i % 2 == 0) {// 唤醒condition.signal();try {// 等待condition.await();} catch (InterruptedException e) {e.printStackTrace();}}}} finally {// 释放锁lock.unlock();}}
}class Character implements Runnable {private ReentrantLock lock;private Condition condition;public Character(ReentrantLock lock, Condition condition) {this.lock = lock;this.condition = condition;}@Overridepublic void run() {final ReentrantLock lock = this.lock;// 加锁lock.lock();try {for (char i = 'A'; i <= 'Z'; i++) {System.out.print(i);if (i < 'Z') {// 唤醒数字线程condition.signal();try {// 等待condition.await();} catch (InterruptedException e) {e.printStackTrace();}}}} finally {// 释放锁lock.unlock();}}
}

三、ReentrantLock和synchronized的区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1538697.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

开放的数据时代:Web3和个人隐私的未来

在数字化和信息化的时代&#xff0c;数据隐私成为了公众关注的焦点。随着Web3技术的兴起&#xff0c;个人隐私保护进入了一个新的阶段。Web3作为去中心化的互联网架构&#xff0c;提出了对数据控制和隐私保护的新方案。本文将探讨Web3如何影响个人隐私的未来&#xff0c;并分析…

Vue3中的Pinia——管理应用程序的全局状态

介绍Pinia Pinia 是 Vue.js 的状态管理库&#xff0c;主要用于管理应用程序的全局状态。它是 Vuex 的替代品&#xff0c;提供了更简单和更灵活的 API。Pinia 的主要作用包括&#xff1a; 1. 状态管理&#xff1a;Pinia 允许你在应用中集中管理状态&#xff0c;方便不同组件之…

leetcode:验证回文串

[题目链接] string func(string s)//先将大写转换成小写&#xff0c;并且去除空格等&#xff0c;只保留小写字母 {string tmp;string::iterator it s.begin();while (it ! s.end()){//大写字母if (*it < 90 && *it>65)//A-Z的ASCII码为65-90{tmp *it 32;//a-z…

Redis存储原理

前言 我们从redis服务谈起&#xff0c;redis是单reactor&#xff0c;命令在redis-server线程处理。还有若干读写IO线程负责IO操作&#xff08;redis6.0之后&#xff0c;Redis之pipeline与事务&#xff09;。此外还有一个内存池线程负责内存管理、一个后台文件线程负责大文件的关…

大数据Flink(一百一十八):Flink SQL水印操作(Watermark)

文章目录 Flink SQL水印操作&#xff08;Watermark&#xff09; 一、为什么要有WaterMark 二、​​​​​​​​​​​​​​Watermark解决的问题 三、​​​​​​​​​​​​​​代码演示 Flink SQL水印操作&#xff08;Watermark&#xff09; 一、​​​​​​​为什么…

《黑神话悟空》黄眉打法技巧图文攻略详解

​黄眉是黑神话悟空第三章的关底的boss&#xff0c;很多的玩家都非常的好奇这个boss到底要怎么打&#xff0c;这里小编就为大家带来了黄眉这个boss的打法&#xff0c;我们不要使用法术&#xff0c;只使用禁字诀就可以击败这个boss&#xff0c;详细的内容可以在这里进行了解和查…

DevEco Profiler调优工具(二)

一、Profiler调优模板 3、Snapshot Insight 4、CPU Insight 5、Frame Insight 6、Launch Insight

硬件(驱动开发)

一、OSC基本架构&#xff08;片上系统&#xff09; OSC&#xff08;On-chip System Control&#xff0c;片上系统控制&#xff09;基本架构通常涉及片上系统中的各个组件如何进行协调与控制&#xff0c;以实现高效的处理、通信和管理。OSC架构在现代微处理器和系统单芯片&…

WebApi开发中依赖注入和RESTful 详解

Web API 开发中的依赖注入和 RESTful 详解 在现代 Web API 开发中&#xff0c;依赖注入&#xff08;Dependency Injection, DI&#xff09;和 RESTful 架构 是两个极为重要的概念。本文将详细探讨它们的定义、应用场景及在 Web API 开发中的最佳实践。 一、依赖注入 (Depende…

[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking/手势跟踪)

前言 最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能&#xff0c;但是遇到了如下问题&#xff1a; 在Unity里面没法串流调试眼动追踪功能&#xff0c;根本获取不到Device&#xff0c;只能将整个场景build成APK&#xff0c;安装到头盔里&#xff0c;才能在…

【java面向对象二】static(一)

文章目录 前言一、static修饰成员变量二、static修饰成员变量的应用场景三、static修饰成员方法四、搞懂main方法总结 前言 学习static修饰类变量&#xff0c;类方法&#xff0c;以及main方法的使用。 一、static修饰成员变量 static 叫静态&#xff0c;可以修饰成员变量&…

高密原型验证系统解决方案(下篇)

0 引言 我们在上篇中和大家探讨了用户在进行大规模 复杂 SoC 设计原型验证时在全局时钟及复位同步&#xff0c; 大规模设计分割以及高速接口与先进 Memory 控制 器 IP 验证等方面遇到的关键困难&#xff0c;并提出了相应的 解决方案帮助用户来克服这些困难。接下来我们会 和用户…

Django ORM(多表)

文章目录 前言一、关联关系模型二、一对多写入数据二、多对多写入数据二、跨表查询1.查找test 标签的文章2.查找作者名为 test 的文章及标签 三、跨表删除 前言 表与表之间的关系可分为以下三种&#xff1a; 一对一: 一对一关系表示一个模型的每个实例与另一个模型的每个实例…

华为OD机试 - 字符串划分(Java 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;E卷D卷A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加…

Python | Leetcode Python题解之第416题分割等和子集

题目&#xff1a; 题解&#xff1a; class Solution:def canPartition(self, nums: List[int]) -> bool:n len(nums)if n < 2:return Falsetotal sum(nums)if total % 2 ! 0:return Falsetarget total // 2dp [True] [False] * targetfor i, num in enumerate(nums…

最低成本的游戏串流方案分享 如何自己打造云电脑?

今天教大家如何最低成本实现串流 出门在外也可以随时随地游玩端游大作 硬件准备&#xff1a;一台电脑 手机/平板一台 软件&#xff1a;Gameviewer远程 为啥不用moonlight等其他软件呢 因为设置公网穿透等复杂操作对小白来说不太友好 而GameViewer从安装到使用仅需一键 对比同类…

MATLAB系列07:输入/输入函数

MATLAB系列07&#xff1a;输入/输入函数 8. 输入/输入函数8.1 函数textread8.2 关于load和save命令的进一步说明8.3 MATLAB文件过程简介8.4 文件的打开和关闭8.4.1 fopen函数8.4.2 fclose函数 8.5 二进制 I/O 函数8.5.1 fwrite 函数8.5.2 fread函数 8.6 格式化 I/O 函数8.6.1 f…

【C++ 差分数组 前后缀分解】P7404家庭菜园

本文涉及知识点 C差分数组 C前后缀分解 P7404家庭菜园 出自洛谷&#xff0c;我简述一下。 已知数组a&#xff0c;长度为n(1<n<2e5),1 <a[i] <1e9。一次操作如下&#xff1a;将a[i…j]全1。问最少操作多少次&#xff0c;使得a成为山形数组&#xff0c;即存在k&am…

力扣题解2332

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述&#xff08;中等&#xff09;​&#xff1a; 坐上公交的最晚时间 给你一个下标从 0 开始长度为 n 的整数数组 buses &#xff0c;其中 buses[i] 表示第 i 辆公交车的出发时间。同时给你一…

算法题——最小会议室数量

题目1: 1.假定会议时间从凌晨零点开始&#xff0c;即 0<si<ei<1440&#xff0c;si和ei代表当天某一时刻从零点开始计算的开始和结束分钟数&#xff0c;如[90,360]&#xff0c;表示会议时间从1:30到6:00。 2.不考虑房间的容量问题&#xff0c;并且公司为了提升大家的体…