java并发编程常见锁类型

锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。锁是解决并发冲突的重要工具。在开发中我们会用到很多类型的锁,每种锁都有其自身的特点和适用范围。需要深刻理解锁的理念和区别,才能正确、合理地使用锁。
常用锁类型
乐观锁与悲观锁
悲观锁对并发冲突持悲观态度,先取锁后访问数据,能够较大程度确保数据安全性。
而乐观锁认为数据冲突的概率比较低,可以尽可能多地访问数据,只有在最终提交数据进行持久化时才获取锁。
悲观锁总是先获取锁,会增加很多额外的开销,也增加了死锁的几率。尤其是对于读操作,不会修改数据,使用悲观锁大大增加系统的响应时间。
乐观锁最后一步才提交数据,死锁的几率比较低,但是如果有多个事务同时处理相同数据也有几率会冲突甚至导致系统异常。
传统关系型数据库常常使用悲观锁,以提高数据安全性。使用乐观锁的场景,通常用版本号来确保数据安全。
自旋锁
自旋锁会让处于等待状态的线程执行空循环一段时间,执行完空循环后如果能够获取锁就立即获取锁,否则才挂起线程。
使用自旋锁,能够降低等待线程被挂起的概率。线程进入阻塞状态再次唤醒,需要在用户态和内核态之间进行切换,自旋锁避免了进入内核态,因此有比较好的性能。
自旋锁适用于竞争不激烈且线程任务执行时间短的场景。但是对于竞争激烈或者任务执行时间长的场景,不适合使用自旋锁,否则会浪费 CPU 时间片。
重入锁
Java 中提供的可重入锁 ReentrantLock,是一种递归无阻塞的同步机制,可以在外层方法已经加锁的情况下,让内层方法再次获取锁。
ReentrantLock 维护了一个计数器,每加锁一次计数器加一,解锁一次计数器减一。Java 中的 synchronized 也是一种可重入锁。
轮询锁与定时锁
轮询锁是通过线程不断尝试获取锁来实现的,可以避免发生死锁,可以更好地处理错误场景。Java 中可以通过调用锁的 tryLock 方法来进行轮询。tryLock 方法还提供了一种支持定时的实现,可以通过参数指定获取锁的等待时间。如果可以立即获取锁那就立即返回,否则等待一段时间后返回。
读写锁
读写锁 ReadWriteLock 可以优雅地实现对资源的访问控制,具体实现为 ReentrantReadWriteLock。读写锁提供了读锁和写锁两把锁,在读数据时使用读锁,在写数据时使用写锁。
读写锁允许有多个读操作同时进行,但只允许有一个写操作执行。如果写锁没有加锁,则读锁不会阻塞,否则需要等待写入完成。
对象锁与类锁
能锁对象,就不要锁定类,尽量控制范围。锁定类以后,所有的线程使用同一把锁,同一时刻只有一个线程可以加锁;而锁定对象,可以增加锁的数量,提高并发的效率。
注意事项
锁的公平性
大部分锁都支持设置公平性:公平锁是指按照线程等待的时间来决定哪个线程先获取锁,非公平锁是指随机选择一个线程来获取锁。重入锁和读写锁默认都是非公平锁,也可以通过参数来设置。使用时需要根据具体场景来决定设置公平或非公平。
锁消除
如无必要,不要使用锁。Java 虚拟机也可以根据逃逸分析判断出加锁的代码是否线程安全,如果确认线程安全虚拟机会进行锁消除提高效率。
锁粗化
如果一段代码需要使用多个锁,建议使用一把范围更大的锁来提高执行效率。Java 虚拟机也会进行优化,如果发现同一个对象锁有一系列的加锁解锁操作,虚拟机会进行锁粗化来降低锁的耗时。
以上就是一些常见的锁型,获取更多的知识详解和方法,可以私信评论我,大家一起学习进步!

原文地址:https://blog.51cto.com/14623707/2462554

时间: 2024-10-07 16:51:15

java并发编程常见锁类型的相关文章

Java并发编程:锁的释放

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

并发编程艺术-锁类型以及底层原理

Java并发编程艺术-并发机制的底层原理实现 1.Volatile 定义: Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量. volatile借助Java内存模型保证所有线程能够看到最新的值.(内存可见性) 实现原理: 将带有volatile变量操作的Java代码转换成汇编代码后,可以看到多了个lock前缀指令(X86平台CPU指令).这个lock指令是关键,在多核处理器下实现两个重要操作: 1.将当前处理器缓存行的数据写回到系

java并发编程使用锁进行数据同步操作一

项目中总是出现招标项目超投的情况,最开始总是觉得应该使用框架Hibernate自带的并发策略中的乐观锁(version)解决问题,参考了很多网上的资料,也参考了Hibernate的帮助文档,由于对Hibernate乐观锁机制不了解,问题就一直没有解决. 最近在看Java并发编程相关知识,了解了些许并发,线程,锁的知识.想到了这个问题,曾经使用Synchroized关键字时总是苦于无法获取同一个对象,导致解决方案无效.这次采用的方案是:创建了静态的HashMap<Integer,Lock>,初始

Java并发编程-各种锁

安全性和活跃度通常相互牵制.我们使用锁来保证线程安全,但是滥用锁可能引起锁顺序死锁.类似地,我们使用线程池和信号量来约束资源的使用, 但是缺不能知晓哪些管辖范围内的活动可能形成的资源死锁.Java应用程序不能从死锁中恢复,所以确保你的设计能够避免死锁出现的先决条件是非常有价值. 一.死锁 经典的"哲学家进餐"问题很好的阐释了死锁.5个哲学家一起出门去吃中餐,他们围坐在一个圆桌边.他们只有五只筷子(不是5双),每两个人中间放有一只. 哲学家边吃边思考,交替进行.每个人都需要获得两只筷子才

转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接口有3个实现它的类:ReentrantLock.ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁.读锁和写锁.lock必须被显式地创建.锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例化

Java并发编程与技术内幕:聊聊锁的技术内幕(上)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.基础知识 在Java并发编程里头,锁是一个非常重要的概念.就如同现实生活一样,如果房子上了锁.别人就进不去.Java里头如果一段代码取得了一个锁,其它地方再想去这个锁(或者再执行这个相同的代码)就都得等待锁释放.锁其实分成非常多.比如有互斥锁.读写锁.乐观锁.悲观锁.自旋锁.公平锁.非公平锁等.包括信号量其实都可以认为是一个锁. 1.什么时需要锁呢? 其实非常多的场景,如共享实例变量.共

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

【Java并发编程实战】-----“J.U.C”:CLH队列锁

在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QNode中包含有一个locked的字段,该字段表示该节点是否需要获取锁,为true表示需要获取,为false表示不需要获取.在CLH队列中,节点与节点之间并不是通过next指针来连接的而是通过myPred所指向节点的变化情况来影响的myNode的行为. 假设有两个线程(线程A.线程B).开始线程A需要

Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

Java并发编程系列[未完]: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) 一.重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理.现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的.而操作系统实现线程之间的切换这就需要从用户态转换到核心