synchronized和ReentrantLock傻傻分不清楚

server/2024/11/15 6:17:43/

synchronized和ReentrantLock都是用于线程间同步的机制,都是可重入锁(同一个线程可以多次获取同一个锁),它们的异同点如下:
一、应用场景
1.synchronized可应用于实例方法、静态方法和代码块。
2.ReentrantLock 是 java.util.concurrent.locks 包下的一个具体类,实现了 Lock 接口。使用时需要显式创建 ReentrantLock 对象并调用其方法。


二、锁获取与释放机制
1.当进入或退出同步代码时,synchronized自动获取锁释放锁,执行完毕或抛出异常时自动释放锁。
2.ReentrantLock需要手动调用lock()获取锁,调用unlock()释放锁。若没有主动释放锁,可能导致死锁。推荐使用 try-catch-finally 或 try-with-resources 结构来确保锁的释放。


三、尝试非阻塞地获取锁:
1.synchronized 无法做到尝试非阻塞地获取锁。
2.ReentrantLock 提供了tryLock()方法,该方法尝试获取锁,如果成功则返回true,否则立即返回false,线程不会被阻塞。


四、锁的公平性
1.synchronized是非公平的,即在锁被释放时,任何一个等待锁的线程都有机会获得锁。
2.ReentrantLock默认情况下也是非公平的,但可以通过带布尔值为true的构造函数构造成公平锁。当ReentrantLock被配置为公平锁时,则多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。


五、条件等待与通知
1.synchronized 通过 Object 类的 wait(), notify(), notifyAll() 方法来实现线程间的条件等待与通知。这些方法必须在 synchronized 代码块或synchronized修饰的方法中调用,否则会抛出 IllegalMonitorStateException。
2.ReentrantLock 可以通过 newCondition() 方法创建多个条件变量 Condition 对象。线程可以调用 condition.await() 进行等待,其他线程调用 condition.signal() 或 condition.signalAll() 进行通知。这种方式支持更精细的线程间协作,可以避免"伪唤醒"问题,使得线程等待特定条件而不是仅仅等待锁的释放。


六、可中断性
1.synchronized不支持正在等待锁的线程被中断。
2.ReentrantLock提供了lockInterruptibly()方法支持等待锁的线程被中断(若所在的线程被中断,则会抛出异常并释放当前获得的锁)。当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。


七、可配置性
1.在synchronized中,锁对象wait()、notify()或notifyAl()只能关联一个隐含的条件Condition,若要和多于一个的条件Condition关联不得不额外地添加一个锁。不可设置超时时间。
2.一个ReentrantLock对象可以使用tryLock(long timeout, TimeUnit unit)设置超时时间(超时后线程不会一直阻塞,而是立即返回一个布尔值表示是否成功获取锁),可以使用getOwner()或isHeldByCurrentThread()判断锁是否被本线程或其他线程持有,可以使用getQueuedThreads()获取等待此锁的线程集合,可以使用getWaitingThreads(Condition condition)获取等待在此锁上的某个Condition上的线程集合 ,可以通过多次调用newCondition()同时绑定多个Condition对象实现线程等待/通知机制。


八、性能
在高并发场景下,ReentrantLock的性能可能优于synchronized,因为它提供了更多的灵活性和控制选项。


九、锁优化机制
1.synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、重量级锁。
2.ReentrantLock 的实现则是通过利用 CAS(Compare And Swap)自旋机制保证线程操作的原子性和 volatile 保证数据可见性以实现锁的功能。


十、引申
1.synchronized 是JVM层面的同步,JVM会优化其性能,例如锁消除、锁粗化等。
2.ReentrantLock 是一个类,可以扩展,例如 ReentrantReadWriteLock 就是在 ReentrantLock 基础上实现的读写锁,提供了更复杂的读写权限控制。
3.synchronized在JVM中是采用 monitorenter 和 monitorexit 两个指令来实现同步的,monitorenter 指令相当于加锁,monitorexit 相当于释放锁。而 monitorenter 和 monitorexit 就是基于 Monitor 实现的。
4.ReentrantLock的常用的方法如下:
tryLock():尝试获取锁
getHoldCount():查询当前线程执行 lock() 方法的次数
getQueueLength():返回正在排队等待获取此锁的线程数
isFair():该锁是否为公平锁
hasQueuedThread(Thread thread):返回指定的线程是否在等待获取此锁
5.ReentrantLock中lock() 和 lockInterruptibly() 的区别:
获取锁的过程中如果所在的线程中断,lock() 会忽略异常继续等待获取锁,而 lockInterruptibly() 则会抛出 InterruptedException 异常。
6.synchronized实现锁升级的过程:
在锁对象的对象头里面有一个 ThreadID 字段,在第一次访问的时候 ThreadID 为空,然后JVM让其持有偏向锁,并将 ThreadID 设置为调用锁对象的线程 ID,再次进入的时候会先判断 ThreadID 是否与其线程 ID 一致,如果一致则可以直接使用,如果不一致,则升级偏向锁为轻量级锁,通过自旋来获取锁,不会阻塞,执行一定次数之后会升级为重量级锁(映射到操作系统提供的互斥锁Mutex Lock上)。
7.可以通过设置JVM参数UseHeavyMonitors禁用偏向锁和轻量级锁,直接使用重量级锁。
 


微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。我是程序员小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!


http://www.ppmy.cn/server/6475.html

相关文章

【Linux系统编程】第五弹---基本指令(三)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、grep指令 2、zip/unzip指令 3、tar指令 4、bc指令 5、uname指令 6、重要的几个热键 7、拓展指令 总结 1、grep指令 …

es安装中文分词器

下载地址,尽量选择和自己本地es差不多的版本 https://github.com/infinilabs/analysis-ik/releases 下载好,解压,把里面的文件放到es的plugins/ik目录下 把plugin-descriptor.properties文件里的es版本改成自己对应的 再启动es,能…

最新Java面试题3【2024中级】

互联网大厂面试题 1:阿里巴巴Java面试题 2:阿里云Java面试题-实习生岗 3:腾讯Java面试题-高级 4:字节跳动Java面试题 5:字节跳动Java面试题-大数据方向 6:百度Java面试题 7:蚂蚁金服Java…

深度学习基础——残差神经网络(ResNet)

深度学习基础——残差神经网络(ResNet) 1. 定义 残差神经网络(ResNet)是一种深度神经网络结构,由微软研究院的Kaiming He等人于2015年提出。它通过引入残差块(Residual Block)来解决深度神经网…

python爬豆瓣top250电影

文章目录 前言分析与实现1.对豆瓣网网站进行Ajax分析2.发送请求3.进一步筛选(提取) 完整代码 前言 通过这个项目,可以让小白对爬虫有一个初步认识,爬取豆瓣top250是一个初学者学爬虫的必经之路,话不多说,我…

数据压缩技术:赫夫曼编码原理与实现

数据压缩技术:赫夫曼编码原理与实现 赫夫曼编码的基本原理赫夫曼编码的构建步骤伪代码示例C语言实现示例赫夫曼编码的效率分析赫夫曼编码的应用结语 赫夫曼编码是一种广泛应用于数据压缩的贪心算法,它根据数据中各个符号出现的频率来构建一棵最优二叉树&…

jvm学习笔记

文章目录 1.内存泄漏原因2.JVM部分配置参数3.垃圾回收器的选择4.GC调优:5.查看线程命令6.问题一:cpu高占用7.问题二:接口响应时间很长8.问题三:线程不可用 面试篇1.什么是jvm2.字节码文件的组成3.什么是运行时数据区4.哪些区域会出…

SQLite去除.db-shm和.db-wal文件【已解决】

原因是开启了WAL 日志模式,实现日志回滚功能; 如果是多个连接访问数据库就会出现共享内存-shm文件 PRAGMA journal_modeWAL; 解决办法就是设置为默认模式 PRAGMA journal_modeDELETE; 执行成功后断开再重新连接,就不会出现这两个文件了…