JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

浅谈java锁

wys521 2024-11-21 22:24:09 精选教程 26 ℃ 0 评论

自旋锁

当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。

优缺点

自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的。不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。

阻塞锁

在JAVA环境中,线程Thread有如下几个状态:

  • 新建状态
  • 就绪状态
  • 运行状态
  • 阻塞状态
  • 死亡状态

阻塞锁让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间) 时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程,通过竞争,进入运行状态。JAVA中,能够进入 / 退出、阻塞状态或包含阻塞锁的方法有 ,synchronized 关键字(其中的重量锁),ReentrantLock,Object.wait() / notify() ,LockSupport.park() / unpart()

优缺点

阻塞的线程不会占用CPU时间, 不会导致 CPU占用率过高。进入时间以及恢复时间都要比自旋锁略慢。在竞争激烈的情况下,阻塞锁的性能要明显高于自旋锁。

在线程竞争不激烈的情况下,使用自旋锁;竞争激烈的情况下使用,阻塞锁

可重入锁

也叫递归锁,同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。 “独占”,就是在同一时刻只能有一个线程获取到锁,而其它获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁。“可重入”,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。

ReentrantLock 和synchronized 都是可重入锁。

Synchronized和ReentrantLock区别

1)性能:

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized, synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

2)原理:

Synchronized: 进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

ReentrantLock: 是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能

等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。

公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

读写锁

ReadWriteLock同Lock一样也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个是只读的锁,一个是写锁。

读写锁比互斥锁允许对于共享数据更大程度的并发。与互斥锁相比,读写锁是否能够提高性能取决于读写数据的频率、读取和写入操作的持续时间、以及读线程和写线程之间的竞争。

  • 读-读能共存,
  • 读-写不能共存,
  • 写-写不能共存。

锁降级

读写锁支持锁降级,遵循按照获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁,不支持锁升级

互斥锁

互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。同步是协调多个相互关联线程合作完synchronized不同用法锁对象说明。

synchronized和Lock是互斥锁

修饰在静态方法上,锁对象是当前类的Class对象修饰在实例方法上,锁对象是当前实例对象同步块中,锁对象是synchronized括号后面的对象成任务,彼此之间知道对方存在,执行顺序往往是有序的。

悲观锁、乐观锁

悲观锁

假设会发生并发冲突,屏蔽一切可能违反数据完整性的操作(具有强烈的独占和排他性) 依赖数据库的锁机制实现,以保证操作最大程度的独占性。数据库性能的大量开销,特别是对长事务而言,这样的开销无法承受。sql后面加上 for update或者for update nowait。

独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

乐观锁

假设不会发生并发冲突,只有在提交操作时检查是否违反数据完整性,乐观锁不能解决脏读问题

乐观锁大多都基于数据版本(version)记录机制实现,数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据表增加一个“version”字段来实现。读取出数据时,将此版本一同读出,之后更新时,对此版本后 +1。将提交的版本数据与数据库表对应记录的当前版本信息对比时,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。

优缺点

可以多个事务同时进行,然后根据返回的不同结果做相应的操作,避免了长事务中的数据库加锁开销。

乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性。

公平锁、非公平锁

公平锁

加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得。

非公平锁

线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。

非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。


Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表