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

news2024/11/12 23:15:33

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

        答案是肯定可以获取同一个锁。因为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;
    }

    @Override
    public 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;
    }

    @Override
    public 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.coloradmin.cn/o/2145519.html

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

相关文章

HbuilderX自定义快捷键

打开快捷键设置&#xff1a;工具->自定义快捷键 复制想要修改的快捷键设置 修改后记得保存 可以通过该方法找到对应功能的系统默认快捷键

[数据集][目标检测]智慧养殖场肉鸡健康状态检测数据集VOC+YOLO格式4657张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4657 标注数量(xml文件个数)&#xff1a;4657 标注数量(txt文件个数)&#xff1a;4657 标注…

用ASR PRO离线语音芯片和月饼盒做一个会跑会跳会说话的机器狗

中秋节刚过&#xff0c;大家月饼盒应该还有&#xff0c;不要扔&#xff0c;可以做点小玩意。 机器狗的创意来自B站石桥北的视频&#xff0c;他使用了一块ESP32芯片和打印件加四个舵机实现&#xff0c;应该说是比较复杂的&#xff0c;需要有3D打印机打印外壳&#xff0c;还得会…

Hadoop的一些高频面试题 --- hdfs、mapreduce以及yarn的面试题

文章目录 一、HDFS1、Hadoop的三大组成部分2、本地模式和伪分布模式的区别是什么3、什么是HDFS4、如何单独启动namenode5、hdfs的写入流程6、hdfs的读取流程7、hdfs为什么不能存储小文件8、secondaryNameNode的运行原理9、hadoop集群启动后离开安全模式的条件10、hdfs集群的开机…

TortoiseSVN图标不显示的解决

解决办法一&#xff1a;修改svn软件的图标设置 1、选中一个文件夹或在桌面空白处&#xff0c;右击进入svn的setting 2、进入setting->Icon Overlays&#xff0c;Status cache选择Default或shell&#xff0c;然后点击应用 3、查看文件&#xff0c;图标可以正常显示 解决办法…

ubuntu下载安装部署docker,ubuntu下载最新的docker

1.#如果Ubuntu自带的Docker版本太低&#xff0c;我们需要卸载旧版本并安装新的 sudo apt-get remove docker docker-engine docker.io containerd runc2.# 备份原有软件源 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak3.选择合适的镜像源 # 或者使用清华大学sudo…

利用WPF绘制轮廓并保存为图片

1.前言 WPF作为显示工具也挺好用&#xff0c;用C#开发应用软件会比较省力&#xff0c;当然也有其缺点&#xff0c;如在对效率要求较高的情况下有性能问题&#xff0c;本文记录用WPF绘制轮廓并保存为图片相关内容。 显示效果也还不错&#xff0c;满足调试使用了&#xf…

变电站缺陷数据集8307张,带xml标注和txt标注,可以直接用于yolo训练

变电站缺陷数据集8307张&#xff0c; 带xml标注和txt标注&#xff0c;可以直接用于yolo训练&#xff0c;赠附五个脚本 变电站缺陷数据集 数据集概述 变电站缺陷数据集是一个专门针对变电站设备和环境缺陷检测的图像数据集。该数据集包含了8307张经过标注的图像&#xff0c;旨…

Maven和Springboot初识

&#xff08;一&#xff09;Maven Maven是一个项目管理工具&#xff0c;通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的项目管理工具 &#xff08;可以通过pom.xml文件的配置来获取jar包&#xff0c;而不用手动添加&#xff09; Maven可以提高我们的开发效率减少…

Mistral AI 又又又开源了闭源企业级模型——Mistral-Small-Instruct-2409

就在不久前&#xff0c;Mistral 公司在开源了 Pixtral 12B 视觉多模态大模型之后&#xff0c;又开源了自家的企业级小型模型 Mistral-Small-Instruct-2409 &#xff08;22B&#xff09;&#xff0c;这是 Mistral AI 最新的企业级小型模型&#xff0c;是 Mistral Small v24.02 的…

C++源代码封装成dll动态链接库,并在WPF项目中使用的步骤说明

文章目录 1. 创建并生成C的DLL&#xff08;C动态链接库&#xff09;&#xff08;1&#xff09;新建项目-->开发语言选定C&#xff0c;在搜索栏搜索“动态链接库”-->配置项目名称和路径-->添加类&#xff0c;此处命名为My_C_Class&#xff08;2)实现类的功能&#xff…

Elasticsearch 下载安装及使用总结

官网文档地址&#xff1a;Elasticsearch Guide [8.13] 官网下载地址&#xff1a;Download Elasticsearch 1. 下载安装 1、下载对应系统的版本 这里下载的 Elasticsearch 版本为 8.13.2&#xff0c;Elasticsearch 依赖 Java&#xff0c;因此要先在服务器上安装 JDK&#xff…

SOLIDWORKS® 2025 新增功能 - SIMULATION

SOLIDWORKS Simulation 1常规弹簧连接 • 通过定义仅轴向、各向同性或正交各向异性弹簧&#xff0c; 在曲面之间轻松创建自定义弹簧连接。 • 通过添加自定义合规性提高仿真性能和精度。 优点 利用新的弹簧连接功能&#xff0c; 实现更简单、更逼真的仿真 设置。 2增强了…

Unity 百度AI实现无绿幕拍照抠像功能(详解版)

目录 一、前言 1.抠像效果 2.去哪找百度ai抠图 3.基础流程跳过 二、获取AccessToken 1.什么是Token 2.为什么要获取Token 3.如何获取token 4.解析json 5.完整代码 三、抠像 1.准备地址 2.建立链接&#xff0c;和基本配置 3.图片格式转换 4.开始上传 5.获取回复…

SpringBoot 整合docker,执行容器服务

我使用以下文章的镜像作为演示镜像,读者有自己的镜像可以使用自己的 TencentARC/GFPGAN人脸恢复Ubuntu-22.04搭建(附带Docker镜像)_tencentarc gfpgan-CSDN博客 1. 封装springboot 启动docker容器的方法 public String runDockerCommand(String[] command) {StringBuilder res…

代码随想录Day 49|leetcode题目:42.接雨水、84.柱状图中最大矩形

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 题目题目一&#xff1a;42. 接雨水解题思路&#xff1a;暴力解法双指针优化思路&#xff1a;单调栈解法单调栈处理逻辑 题目二&#xff1a; 84.柱状图中最大的矩形解题思路&#xff1a;暴力解法双…

闯关leetcode——35. Search Insert Position

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/search-insert-position/description/ 内容 Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it wou…

【数据结构】排序算法---冒泡排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 冒泡排序&#xff08;英语&#xff1a;Bubble sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的…

svn回退到以前历史版本修改并上传

svn回退到以前版本&#xff0c;并在以前版本上修改代码后&#xff0c;上传到svn库当中&#xff0c;如下步骤&#xff1a; 3、 以回退到版本号4为例&#xff1a;选中版本号4&#xff0c;右键->Revert to this version,在出现的对话框中 点击yes&#xff01; 4、 5、

【ARM】Trustzone和安全架构

Trustzone的基本概念&背景和历史 什么是Trustzone&#xff1f; 什么是TEE&#xff1f; Trustzone是一个技术&#xff0c;是一个技术的设计&#xff0c;一个安全架构&#xff0c;既不是软件也不是硬件。 TEE (Trusted Execution Environment) 可信执行环境。就是依托Trust…