乐观锁/悲观锁
公平锁/非公平锁
独享锁/共享锁
互斥锁/读写锁
可重入锁(又名递归锁)
自旋锁
分段锁
偏向锁/轻量级锁/重量级锁
乐观锁/悲观锁
乐观锁与悲观锁不是指两种特定类型的锁, 而是人们针对并发同步的角度定义出来的两种概念
乐观锁: 顾名思义, 乐观, 认为拿到的数据没有被别的线程修改, 不上锁, 使用版本号来标记当前拿到的数据, 需要跟新数据时, 再去比较当前版本号与期望版本号是否一致, 常用的乐观锁有数据库的version, jdk1.8并发包中的atomin下的原子操作类, 的CAS实现
悲观锁:顾名思义, 悲观, 认为拿到的数据会被别的线程修改, 所以拿数据的时候先上锁, 然后进行操作, 操作完成释放锁, 常用的悲观锁数据库中for update, java synchronezed关键字, ReetrantLock 加锁
公平锁/非公平锁
公平锁是指多个线程按照申请的顺序来获取锁, 非公平锁不按照申请的顺序来获取锁
synchronezed关键字, ReetrantLock 默认构造器创建的对象都是非公平锁
ReetrantLock(true) 创建的是公平锁
独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有, 共享锁是指一个该锁可以被过个线程所持有
synchronezed关键字, ReetrantLock 都是独享锁
ReadWriteLick其读是共享锁, 写是独有锁
互斥锁/读写锁
独享锁/共享锁是一致广义的说法, 互斥锁/读写锁就是具体的实现
synchronezed关键字, ReetrantLock 都是独享锁
ReadWriteLock其读是共享锁, 写是独有锁
可重入锁(又名递归锁)
线程可以进入任何一个它已经拥有的锁所同步的代码块中, 简单来说就是线程在外层方法获取锁, 进入内层方法会自动获取锁, synchronezed关键字, ReetrantLock都是可重入锁, 好处是避免死锁.
自旋锁
自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
分段锁
分段锁是一种锁的设计, 并不是一种具体的锁, 最有名的分段锁设计是ConcurrentHashMap, 将map容器分段出多个Entry数组, 在push炒作时先确认哪个数据, 然后再进行加锁操作.
synchronized与Lock的区别
- synchronized 是属于JVM层面的关键字, Lock是api层面的锁
- synchronized无需手动去释放锁, Lock需要手动释放锁(可能导致死锁)
- synchronized不可中断, ReentrantLock可中断
- synchronized非公平锁, ReentrantLock可以设置为公平非公平锁
- synchronized无法绑定多个Condition, 无法实现分组唤醒, ReentrantLock可以实现
死锁分析
死锁的原因
线程A持有锁A, 尝试获取线程B, 线程B持有锁A尝试获取线程B.
代码分析
public class Resource implements Runnable {
private String lockA = null;
private String lockB = null;
public Resource(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
public void run() {
synchronized (lockA) {
System.out.println("===" + lockA + "===");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
synchronized (lockB){
System.out.println("===" + lockB + "====");
}
}
}
}
public class LockTest {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new Resource(lockA, lockB), "A").start();
new Thread(new Resource(lockB, lockA), "B").start();
}
}
执行效果: 程序一直没有停止