java并发编程常见锁类型

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

相关文章
相关标签/搜索