Java中的锁

一、乐观锁和悲观锁

悲观锁

一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程可能也会操作这个数据,所以每次操作前都会上锁,这样其他线程想操作这个数据拿不到锁只能阻塞了。

乐观锁

乐观锁操作数据时不会上锁,在更新的时候会判断一下在此期间是否有其他线程去更新这个数据。

乐观锁可以使用版本号机制和和CAS算法实现。

使用场景

乐观锁适用于写比较少的场景,因为不用上锁、释放锁,省去了锁的开销,从而提高了吞吐量。

如果是写多读少的场景,线程间竞争激烈,使用乐观锁就会导致线程不断进行重试,这样可能还会降低了心更难,这种场景使用悲观锁就比较合适。

二、独占锁和共享锁

独占锁

锁一次只能被一个线程持有。如果一个线程对数据加上排它锁后,那么其他线程不能再对该数据加任何类型的锁。活的独占锁的线程既能读数据又能修改数据。

共享锁

锁可以被多个线程持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据在加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据

三、互斥锁和读写锁

互斥锁

独占锁的一种常规实现,是指某一资源同时指允许一个访问者对其进行访问,具有唯一性和排他性。

互斥锁一次只能一个线程拥有互斥锁,其他线程只有等待。

读写锁

共享锁的一种具体实现。读写锁管理一组锁,一个是只读的锁,一个是写锁。

读锁可以再没有写锁的时候被多个线程共同持有,而写锁是独占的。写锁的优先级要高于读锁,一个获得了读锁的线程必能能看到前一个释放的锁更新的内容。

读写锁相比于互斥锁并发程度更高,每次只有一个写线程,但是同时可以有多个线程并发读

四、公平锁和非公平锁

公平锁

多个线程按照申请锁的顺序来获取锁

非公平锁

多个线程获取锁的顺序并不是按照申请锁的顺序,在高并发环境下,有可能造成优先级翻转或者饥饿的状态。

五、可重入锁

又称为递归锁,是指同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁。

六、自旋锁

线程在没有获取锁时不是被直接挂起,二十执行一个忙循环,这个忙循环就是所谓的自旋。

自旋锁的目的是为了减少线程被挂起的几率,因为现成的挂起和唤醒都是耗资源的操作。

如果锁被另一个线程占用的时间比较长,即使自旋了之后当前线程还是会被挂起,忙循环就会变成浪费系统资源的操作,反而降低了整体性能。因此自旋锁不适应锁占用时间长的并发情况。

自适应自旋锁自旋时间不在固定,由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。

七、锁升级

4种锁状态:无锁、偏向锁、轻量级锁、重量级锁,他会锁着多线程的竞争情况逐渐升级,但不能降级。

无锁

无锁状态其实就是上边的乐观锁

偏向锁

Java偏向锁是指他会偏向于第一个访问锁的线程,如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,name线程是不需要重复获取锁的,这种情况下, 就会给线程加一个偏向锁。

轻量级锁

当线程竞争变得比较激烈时,偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的情况很低,通过自旋方式等待上一个线程释放锁。

重量级锁

如果线程并发进一步加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了第三个线程访问时,轻量级锁就会升级为重量级锁,重量级锁会使除了此时拥有锁的线程以外的线程都阻塞。

升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其他线程都会处于阻塞等待状态。

八、锁优化技术

锁粗化

将多个同步块的数量减少,并将单个同步块的作用范围扩大,本质上就是将多次上锁,解锁的请求合并为一次同步请求

锁消除

虚拟机编译器在运行时检测到了共享数据没有竞争锁,从而将这些所进行消除。

posted @   zmy98  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示