Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语

使用 ReentrantLock 替代 synchronized 关键字原 通过调用 ReentrantLock 的 newCondition() 方法可以创建一个条件变量,然后我们可以使用 await() 方法来使线程等待某个条件的发生,使用 signal() 或 signalAll() 方法来唤醒等待的线程。然而,Java 5 引入了一个更强大和灵活的同步机制——ReentrantLock,它提供了与 synchronized 关键字类似的功能,并且还具有更多的特性和优势。通过锁的获取和释放,我们确保了对 count 的操作是互斥的,从而避免了并发访问的问题。 阅读详情

Java 5 引入的 Concurrent 并发库软件包中,提供了 ReentrantLock 可重入同步锁,用来替代 synchronized 关键字原语,并可提供更好的性能,以及更强大的功能。使用方法也很简单:

public final ReentrantLock lock = new ReentrantLock();
......
try {
    lock.lock();
    // 进入同步内容
    ....
} finally {
    lock.unlock(); // 必须在 finally 块中解锁,否则一旦出现异常,执行不到解锁,则一直锁住了。
}

synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。 
1. 某个线程在等待一个锁的控制权的这段时间需要中断【可中断锁】;
2. 需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程【与Linux的条件变量机制相同】;
3. 具有公平锁功能,每个到来的线程都将排队等候;

先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵) 
这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:

public class Buffer {
    private Object lock;
    
    public Buffer() {
        lock = this;
    }
    
    public void write() {
        synchronized (lock) {
            long startTime = System.currentTimeMillis();    
            System.out.println("开始往这个buff写入数据…");
            for (;;) { // 模拟要处理很长时间
                if (System.currentTimeMillis() > Integer.MAX_VALUE) {
                    break;
                }
            }
            System.out.println("终于写完了");
        }
    }
    
    public void read() {
        synchronized (lock) {
            System.out.println("从这个buff读数据");
        }
    }
}

接着,我们来定义2个线程,一个线程去写,一个线程去读。 

public class Writer extends Thread {
    private Buffer buff;
    
    public Writer(Buffer buff) {
        this.buff = buff;
    }
    
    @Override
    public void run() {
        buff.write();
    }
}
     
public class Reader extends Thread {
    private Buffer buff;    
     
    public Reader(Buffer buff) {
        this.buff = buff;
    }
    
    @Override
    public void run() {
        buff.read(); // 这里估计会一直阻塞
        System.out.println("读结束");
    }
}
好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。

public class Test {
    public static void main(String[] args) {
        Buffer buff = new Buffer();
        
        final Writer writer = new Writer(buff);
        final Reader reader = new Reader(buff);
     
        writer.start();
        reader.start();
     
        new Thread(new Runnable() {
            @Override
            public void run() {
                long start = System.currentTimeMillis();
                for (;;) {
                    // 等5秒钟去中断读    
                    if (System.currentTimeMillis() - start > 5000) {
                        System.out.println("不等了,尝试中断");
                        reader.interrupt();
                        break;
                    }
                }
            }
        }).start();
    }    
}
我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。 

import java.util.concurrent.locks.ReentrantLock;
public class BufferInterruptibly {
    private ReentrantLock lock = new ReentrantLock();
    
    public void write() {
        lock.lock();
        try {
            long startTime = System.currentTimeMillis();
            System.out.println("开始往这个buff写入数据…");
            for (;;) { // 模拟要处理很长时间
                if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) {
                    break;
                }
            }
            System.out.println("终于写完了");
        } finally {
            lock.unlock();
        }    
    }
    
    public void read() throws InterruptedException {
        lock.lockInterruptibly();// 注意这里,可以响应中断    
        try {    
            System.out.println("从这个buff读数据");
        } finally {    
            lock.unlock();    
        }    
    }    
} 
当然,要对reader和writer做响应的修改   

public class Reader extends Thread {
    private BufferInterruptibly buff;
     
    public Reader(BufferInterruptibly buff) {
        this.buff = buff;
    }
    
    @Override
    public void run() {
        try {
            buff.read();//可以收到中断的异常,从而有效退出
        } catch (InterruptedException e) {
            System.out.println("我不读了");
        }
        
        System.out.println("读结束");
    }
}
     
/**
* Writer倒不用怎么改动
*/    
public class Writer extends Thread {
    private BufferInterruptibly buff;
    public Writer(BufferInterruptibly buff) {
        this.buff = buff;
    }
     
    @Override
    public void run() {
        buff.write();
    }
}
     
public class Test {
    public static void main(String[] args) {
        BufferInterruptibly buff = new BufferInterruptibly();
     
        final Writer writer = new Writer(buff);
        final Reader reader = new Reader(buff);
     
        writer.start();    
        reader.start();    
     
        new Thread(new Runnable() {
            @Override
            public void run() {
                long start = System.currentTimeMillis();
                for (;;) {
                    if (System.currentTimeMillis() - start > 5000) {
                        System.out.println("不等了,尝试中断");
                        reader.interrupt();
                        break;
                    }
                }
            }
        }).start();
    }
}
这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。

转自:http://hi.baidu.com/cyberniuniu/item/7fdba2fbe9373b733d198b34

OData Request:Create Entity 在 ABAP OData Client Proxy 中的工程化实践 文章摘要 本文针对具备 SAP Gateway 与 ABAP 开发经验的读者,详细讲解如何通过 ABAP OData Client Proxy 实现 OData 服务的 Create 操作。首先对比了 OData V2 与 V4 的 Create 规范差异,强调键、集合 URL 和请求体的标准化要求。随后介绍了 ABAP Client Proxy 的编程模型,包括 Client Proxy、Entity List Resource 和 Create Request/Response 三个核心对象,并提供了完 阅读详情

相关推荐

基于近场动力学的二维疲劳裂纹扩展模型_近场动力学数值模拟的程序实现(1)——引言...

参考书目W. H. Gerstle. 2015. Introduction to Practical Peridynamic: Computational Solid Mechanics Without Stress and Strain.E.Madenci, A.Barut, M.Dorduncu. Peridynamic Differential Operator for Numerical ...

weixin_39764603的博客 1202

分布锁替代方案

synchronized和ReentrantLock等锁机制为Java单体应用提供了在并发情况下程序可正确运行的保障,然应用开发已归治于分布式与微服务久矣,单体应用时代曾作威作福,号令天下线程的锁机制,在遇到多节点部署后,便只可将其位禅于分布锁。难道Java锁机制在分布环境下就真的一点发挥空间也没有了吗?他是否还有机会从分布锁手中夺回统领群“线”的权利,或者至少与其分庭抗礼呢?当然有,不过仅凭他自己是做不到的,他还需要一位路由大将军的辅佐。 到此,估计老鸟应该已经猜到了具体...

wangleimj88的专栏 1231

基于UWB技术的自动跟随避障系统设计研究

基于UWB技术的自动跟随避障系统设计研究 摘要:目前我国针对自动跟随避障系统方面的研究较少,同时国内自动跟随避障系统仍然处在未成熟阶段,多数仍停留在应用蓝牙控制或摄像头捕捉这类跟随方式,但这类方式在实际应用过程中往往会出现易受干扰,精度差,易受环境干扰等问题,这不仅会降低自动跟随避障系统的性能,同时也会对用户造成诸多困扰。因此,就需要结合目前较为有效的UWB技术,对自动跟随避障系统进行设计。基于此,本文首先将会针对UWB技术展开概述,而后对基于UWB技术的自动跟随避障系统组成展开分析,进而对基于UWB技术的

qq_43543515的博客 7992

关于Java中同步锁的代替

使用这些机制和API可以在不少场合替代传统的锁,以实现更高效和可伸缩的并发控制。选择哪种机制取决于具体的应用场景和性能要求。在实际开发中,建议结合程序的具体需求和性能测试结果,选择最适合的并发控制机制。以下是一些举例:(1)使用AtomicInteger进行原子操作// 原子地增加计数器的值// 获取当前计数器的值(2)使用ConcurrentHashMap进行并发映射操作(3)使用进行读写分离try {try {

qq_64179563的博客 2294

AirSim中图像API的应用

解析Airsim图像获取保存的api

fegxg的博客 1605

AI+医疗:多模态模型让肺结节诊断准确率突破95%+的实战方案

多模态融合是必由之路:单一影像模型难以突破95%准确率,必须整合临床、生物标志物等多维数据跨模态注意力机制是核心:简单拼接效果差,注意力机制能自动学习各模态权重,提升融合效果临床适配比技术先进更重要:模型输出必须符合医生诊断习惯,提供可解释的决策依据持续学习是长期成功的关键:医疗数据分布不断变化,模型必须具备自适应能力多学科协作是保障:放射科、胸外科、病理科、AI工程师共同参与模型设计与优化。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1022

(转)深入研究ReentrantLock(重入锁)之引出话题篇

synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。 1.某个线程在等待一个锁的控制权的这段时间需要中断 2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程 3.具有公平锁功能,每个

guobaoaskformore的专栏 6029

保姆级教程:在RK3588开发板上编译带MPP硬件加速的FFmpeg(含完整依赖库配置)

本文提供在RK3588开发板上编译支持MPP硬件加速的FFmpeg的详细教程,涵盖交叉编译环境搭建、MPP框架及依赖库配置,最终实现4K视频硬件编解码性能优化。通过实战步骤和性能对比,展示如何充分发挥RK3588的硬件加速能力。

weixin_30839881的博客 418

使用 ReentrantLock 替代 synchronized 实现更灵活的锁控制

我们可以显式地调用 lock() 方法获取锁,以及 unlock() 方法释放锁,从而在更精细的粒度上控制锁的获取和释放。这类似于 synchronized 中的 wait()、notify() 和 notifyAll() 方法,但 Condition 提供了更灵活的控制,比如可以创建多个 Condition 对象,针对不同的条件进行等待和通知。确保在获取锁和释放锁的代码块中不会出现死循环或者长时间阻塞的情况,同时在 finally 块中释放锁是至关重要的,以保证锁能够被正确释放。

Techlifehacks的博客 355

新手必看!无人机定高PID调参避坑指南:从理论到实战的完整流程

本文为无人机新手提供定高PID调参的完整指南,从传感器融合到抗风策略,详细解析多传感器融合、PID控制参数调试及环境干扰应对。通过实战案例和代码示例,帮助开发者快速掌握无人机定高技术,避免常见调参陷阱,提升飞行稳定性。

spark7igniter的博客 221

ICM20948 DMP代码详解(69)

ICM20948 DMP代码详解(69)

phmatthaus的专栏 617

使用原子类替代synchronized和Lock(重量级锁)

我们都知道,synchronized和Lock是比较重量级的锁,多线程进行操作时,会锁住一段代码,这样高并发下效率就比较低。而juc提供了原子类和volatile,就是解决重量级锁的效率问题,原子类会调用native方法,native方法调用操作系统的接口(汇编指令:cmpxchg),在硬件层面对变量进行加锁,这种细粒度的锁,效率更高。而volatile保证了多线程之间变量的可见性,修改变量后立即写回到主存并让其他线程可见。

Xiaofeng_Lu__的博客 601

基于Keil软件实现对射式红外传感器计次 (江协科技HAL库)

用HAL库复现江协科技,让嵌入式学习之路更顺畅。

2401_82728381的博客 798

【Java 开发日记】我们来说一下 synchronized 与 ReentrantLock 的区别

我是小假 期待与你的下一次相遇 ~

纵有疾风起 人生不言弃 1万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Java并发编程必备:Synchronized关键字深入解析

在Java多线程编程中,线程安全是非常重要的一个概念。为了防止多个线程同时访问共享资源时出现数据不一致或其他竞态条件问题,Java提供了关键字和监视器锁(Monitor Lock)机制。这个博客将深入探讨的原理、使用场景以及与监视器锁的关系。通过本文,我们深入探索了Java中的关键字,包括其互斥性和可重入性的特性。文章详细介绍了的三种使用方式:修饰代码块、修饰普通方法和修饰静态方法。同时,我们还解析了监视器锁(Monitor Lock)的底层实现,以及与的对比。

2302_81090306的博客 1186

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

java并发编程synchronized、volatile关键字及ReentrantLock类总结

1、java为解决并发问题引入的关键字synchronized, volatile ,怎么用? synchronized 修饰的方法 或者 代码块(保证可见性和排他性); synchronized修饰静态方法时(或方法时)同步的是这个对象类级别的;synchronized修饰方法时,同步的是对象实例级别的 volatile修饰变量(仅保证可见性); 每个线程运行时都有一个线程栈,线程栈

abasen的专栏 2428

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java并发编程:深入对比synchronized与ReentrantLock

本文将深入对比Java并发编程中的两种同步机制:synchronized与ReentrantLock。synchronized是Java内置关键字,使用简单但功能有限;ReentrantLock是显式锁,提供更丰富的特性如公平锁、可中断获取和条件变量。两者都是可重入的,在性能上差异不大。synchronized适合简单同步场景,而ReentrantLock更适合需要高级特性的复杂场景。文章详细分析了两者的实现原理、使用方式及适用场景,并给出了最佳实践建议,帮助开发者根据实际需求做出合理选择。

私聊前往站内信:https://i.youkuaiyun.com/#/msg/chat/weixin_44976692 1040

Java同步机制深度解析: synchronized vs ReentrantLock

在Java并发编程中,同步机制是确保线程安全的关键。本文将深入探讨Java中两种主要的同步机制: synchronized关键字和ReentrantLock类。我们将详细分析它们的实现原理、特性、优缺点,以及适用场景。

2303_76892351的博客 1565

ReentrantLock与Synchronized 使用

ReentrantLock包含两个try Lock()方法:tryLock()tryLock(long timeout, TimeUnit unit)timeout为最长等待时间,TimeUnit为时间单位。

ThisIsMirror的博客 1219

Synchronized vs ReentrantLock

摘要: 本文比较了Java中的两种锁机制:synchronized和ReentrantLock。synchronized是JVM内置的隐式锁,基于Monitor实现,支持锁升级(偏向锁→轻量级锁→重量级锁),使用简单但功能有限。ReentrantLock是显式锁,基于AQS实现,提供更丰富的功能如可中断、公平锁等。文章详细解析了synchronized的底层原理(包括字节码指令、锁状态转换)和ReentrantLock的AQS实现机制,并区分了对象锁(实例级别)和类锁(Class级别)的概念及其应用场景。

qq_41893505的博客 1238

synchronized关键字的作用、使用场景及锁升级过程。ReentrantLock与synchronized的区别及适用场景。

synchronized关键字的作用、使用场景及锁升级过程。

weixin_53180424的博客 1228

基于ReentrantLock的Java并发编程探索

并发编程是指在计算机系统中,同时执行多个独立的任务或操作的一种编程方式。在现代计算机系统中,利用多核处理器、分布式系统等技术,实现并发性已经成为常态。提高系统性能:通过并发编程,可以充分利用计算资源,同时执行多个任务,提高系统的吞吐量和响应速度。例如,可以将一个大型任务拆分成多个子任务并行执行,加快整体处理速度。提升用户体验:对于需要实时响应的应用程序,如网络服务器、图形界面程序等,采用并发编程可以避免因单线程阻塞而导致的界面卡顿或响应延迟,提升用户体验。资源利用率提高。

q1372302825的博客 1252

Java中的锁机制:ReentrantLock 和 synchronized

所谓锁的升级、降级,就是 JVM 优化 Synchronized 运行的机制,当 JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS 操作(compare and swap),在对象头上的 Mark Word 部分设置线程 ID,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。

swadian2008的博客 1141
上一篇: Android 操作系统的内存回收机制
下一篇: Java多线程(九)之ReentrantLock与Condition (2013-02-19 16:43:54)
HabbyGe
博客等级 码龄16年 46粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值