乐观锁、悲观锁

乐观锁

认为读多写少,遇到并发写的可能性低,每次去拿数据都认为别人不会修改,所以不会上锁,但是在更新的时候会判断下在此期间别人有没有去更新这个数据,采用在写时,先读出当前版本号,然后加锁操作,如果失败,则要重复“读-比较-写”的操作。

悲观锁

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


公平锁、非公平锁

公平锁

多个线程按照申请锁的顺序来获取锁。
ReentrantLock(使用 CAS 和 AQS 实现) 通过构造参数可以决定是非公平锁还是公平锁,默认构造是非公平锁;非公平锁的吞吐量性能比公平锁大好。
**

非公平锁

没有顺序完全随机,所以能会造成优先级反转或者饥饿现象。
synchronized 就是非公平锁


重入锁

可重入锁

又名递归锁,指在同一个线程在外层方法获取锁的时候在进入内层方法会自动获取锁,synchronized 和 ReentrantLock 都是可重入锁,可重入锁可以在一定程度避免死锁。参考事务的Required。不需要再进一步获取事务。

不可重入锁

参考required new。可以创建一个新的。


独占锁、共享锁

独占锁

独享锁是指该锁一次只能被一个线程持有
synchronized 和 ReentrantLock 都是独享锁
ReentrantLock 的独享锁和共享锁也是通过 AQS 来实现的

共享锁

该锁可以被多个线程持有
ReadWriteLock 的读锁是共享锁,写锁是独占锁;


互斥锁、读写锁

互斥锁

ReentrantLock实质就是一种互斥锁。当一个线程持有该锁时,其他的线程无法再去竞争获取该锁。

读写锁

ReadWriteLock实质就是读写锁。分为ReadLock和WriteLock两个锁。当加写锁时,会阻塞所有请求,相当于独占锁;但是加读锁时,是不会阻塞其他线程竞争锁的,相当于共享锁。


分段锁

实质是一种锁的设计策略,不是具体的锁,对于 ConcurrentHashMap 而言其并发的实现就是通过分段锁的形式来实现高效并发操作;当要 put 元素时并不是对整个 hashmap 加锁,而是先通过 hashcode 知道它要放在哪个分段,然后对分段进行加锁,所以多线程 put 元素时只要放在的不是同一个分段就做到了真正的并行插入,但是统计 size 时就需要获取所有的分段锁才能统计;分段锁的设计是为了细化锁的粒度。


偏向锁、轻量级锁、重量级锁

这几种锁的概念来自于synchronized锁。详细请阅读文章《Synchronized原理剖析》。
Synchronized原理剖析


自旋锁

其实是相对于互斥锁的概念,互斥锁线程会进入 WAITING 状态和 RUNNABLE 状态的切换,涉及上下文切换、cpu 抢占等开销,自旋锁的线程一直是 RUNNABLE 状态的,一直在那循环检测锁标志位,机制不重复,但是自旋锁加锁全程消耗 cpu,起始开销虽然低于互斥锁,但随着持锁时间加锁开销是线性增长。
对于锁竞争不激烈,而且占用锁时间非常短的代码块来说,可以使用。因为cpu消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发生两次上下文切换。


可中断锁

synchronized 是不可中断的,Lock 是可中断的,这里的可中断建立在阻塞等待中断,运行中是无法中断的。
不可中断时,会通过while循环不停尝试获取锁,对系统性能有较大影响。我们可以通过设置超市时间,或是通过interupt()等方法强制线程中断抢占过程。
详情可参考文章《Lock锁原理剖析》。
Lock锁原理剖析


锁粗化、锁消除

可参考文章《Synchronized原理剖析》