JAVA的锁

  • 乐观锁

乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。java 中的乐观锁基本都是通过UnsafeCAS(Compare And Swap, 翻译过来就是比较并替换)操作实现的;

  • 悲观锁

悲观锁是就是悲观思想,即认为读少写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁;
java中的悲观锁就是synchronizedAbstractQueuedSynchronizer框架下的锁则是先尝试CAS乐观锁去获取锁,获取不到才会转换为悲观锁;

  • 自旋锁

自旋锁原理非常简单, 如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等待有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
线程自旋是需要消耗 cup 的,说白了就是让 cup 在做无用功,如果一直获取不到锁,那线程也不能一直占用 cup 自旋做无用功,所以需要设定一个自旋等待的最大时间。
如果持有锁的线程执行的时间超过自旋等待的最大时间仍没有释放锁,就会导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。

优缺点:

自旋锁尽可能的减少线程的阻塞,这对于锁竞争不激烈锁占用时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发生两次上下文切换!
但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功,占着 XX 不 XX,同时有大量线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要 cup 的线程又不能获取到 cpu,造成 cpu 的浪费。所以这种情况下我们要关闭自旋锁;

  • 偏向锁

在并发环境中,如果某个资源大部分情况都是只有一个线程在访问,那么偏向锁就会提升效率;

偏向锁初始状态并不启动锁机制,而只是在对象的MarkWord上做一个标记,当遇到竞争时,就开始进行锁升级;

所以在竞争激烈的情况下,偏向锁则反而会拖慢速度,因为会多一个“去标记”的过程;基于此,JVM启动时,会延迟几秒才启动偏向锁机制,因为JVM启动过程是在一个多线程竞争的环境中;

  • 条件对象

通常, 线程进人临界区,却发现在某一条件满足之后它才能执行。要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程。当不满足条件时,就调用条件的await()方法,放弃对锁的持有,此时线程将会被阻塞并被加入到该条件的等待集;这种等待状态不会自动清除,而是必须要其他线程通过调用这一条件对象的signalAll()或者signal(),此时等待集中的全部线程都将被激活,一旦获取锁成功,改线程将从await()方法返回从被阻塞的地方开始继续执行。

Java中提供了一些常用的实现:如Object的wait & notify/notifyAllRetreenLock 、ReentrantReadWriteLock及一些其它并发工具

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值