Java锁详解
2021-01-29 21:46:44 1 举报
Java锁分类与关联
作者其他创作
大纲/内容
按照锁的特性划分
如果是非公平锁,后申请的线程可能先获取到锁,是随机或者按照其他优先级排序的。对ReentrantLock类而言,通过构造函数传参可以指定该锁是否是公平锁,默认是非公平锁。一般情况下,非公平锁的吞吐量比公平锁大,如果没有特殊要求,优先使用非公平锁。
AQS
读写锁就是分了两种情况,一种是读时的锁,一种是写时的锁,它允许多个线程同时读共享变量,但是只允许一个线程写共享变量,当写共享变量的时候也会阻塞读的操作。这样在读的时候就不会互斥,提高读的效率。
按照锁的设计划分
分段锁
线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑1 临界区有IO操作2 临界区代码复杂或者循环量大3 临界区竞争非常激烈4 单核处理器
轻量级锁
线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。自旋锁加锁全程消耗 cpu,起始开销虽然低于互斥锁,但随着持锁时间加锁开销是线性增长。自旋锁主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。
不同的划分类型
Java并没有提供任何直接中断某线程的方法,只提供了中断机制。可中断锁在等待获取锁过程中可中断。synchronized 是不可中断的,Lock 是可中断的,这里的可中断建立在阻塞等待中断,运行中是无法中断的。
公平锁
非公平锁
显示锁通过实现java.concurrent.locks.Lock接口
乐观锁与悲观锁: 这个分类不是具体锁的分类,而是看待并发同步的角度;乐观锁则认为对于同一个数据的并发操作是不会发生修改的,在更新数据的时候会采用不断的尝试更新,乐观锁认为不加锁的并发操作是没事的;乐观锁适合读操作非常多的场景,乐观锁其实就是基于 CAS 的无锁编程,譬如java 的原子类就是通过 CAS 自旋实现的。
可中断锁
不可中断锁
可重入锁
不可重入锁
按照锁的状态划分针对synchronized
默认为非公平锁
显示锁通过实现java.util.concurrent.locks.ReadWriteLock
偏向锁
升级
java.util.concurrent.locks.ReentrantReadWriteLock
ReentrantLock类
Java锁
实质是一种锁的设计策略,不是具体的锁,对于 ConcurrentHashMap 而言其并发的实现就是通过分段锁的形式来实现高效并发操作;当要 put 元素时并不是对整个 hashmap 加锁,而是先通过 hashcode 知道它要放在哪个分段,然后对分段进行加锁,所以多线程 put 元素时只要放在的不是同一个分段就做到了真正的并行插入,但是统计 size 时就需要获取所有的分段锁才能统计;分段锁的设计是为了细化锁的粒度。
重量级锁
构造函数的参数
读锁
写锁
共享锁
独占锁/排它锁
自旋锁
互斥锁
CAS
若当前线程执行中已经获取了锁,如果再次获取该锁时,就会获取不到被阻塞。
内置锁通过synchronized实现
悲观锁
乐观锁
又名递归锁所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。synchronized 和 ReentrantLock 都是可重入锁,可重入锁可以在一定程度避免死锁。
如果多个线程申请一把公平锁,那么当锁释放的时候,先申请的先得到,非常公平。
乐观锁与悲观锁: 这个分类不是具体锁的分类,而是看待并发同步的角度;悲观锁认为对于同一个数据的并发操作一定是会发生修改的(哪怕实质没修改也认为会修改),因此对于同一个数据的并发操作,悲观锁采取加锁的形式,因为悲观锁认为不加锁的操作一定有问题;由此可以看出悲观锁适合写操作非常多的场景,不加锁会带来大量的性能提升,悲观锁在 java 中很常见。
0 条评论
下一页