1.锁分类:
1)公平锁/非公平锁
2)可重入锁
3)独享锁/共享锁
4)互斥锁/读写锁
5)乐观锁/悲观锁
6)分段锁
7)偏向锁/轻量级锁/重量级锁
8)自旋锁
2.常见的锁:
- Synchronized:
非公平,悲观、独享、互斥、可重入、重量级锁- ReentrantLock:
默认非公平但可实现公平,悲观、独享、互斥、可重入、重量级锁- ReentrantReadWriteLock:
非公平,悲观、写独享、读共享、读写、可重入、重量级锁
3.ReentrantLock与Synchronized的区别:
- ReentrantLock优势:
原始构成:
Synchronized关键字属于Jvm层面的,底层基于monitor对象锁实现
ReentrantLock属于api接口层面,可中断等待:
线程A和B都要获取对象的锁,如果线程A先获取了锁,B将等待A释放锁,
使用synchronized,如果A不释放,B将一直等待下去,不被中断,
使用ReentrantLock,如果A不释放,B在等待了足够长的时间后,中断等待,去做其它事情
ReentrantLock获取锁的方式:
1)lock(),如果获取了锁立即返回,如果别的线程持有锁,当前线程一直休眠,直到获取锁
2)tryLock(),如果获取了锁,立即返回true(),如果别的线程正持有锁,则立即返回flase
3)tryck(long timeout, TimeUnit unit),如果当前线程获取了锁,立即返回true,如果别的线程持有锁,等待参数给定时间,等待过程中获取了锁,返回true,超时未获取到锁返回false可实现公平锁:
对于ReentrantLock,通过构造函数指定该锁是否是公平锁,默认是非公平锁可绑定多个条件:
绑定多个条件是指一个ReentrantLock可以绑定多个Condition对象,在Synchronized锁对象中,锁对象的wait()和notify()、notifyAll()方法可以实现一个隐含条件,如果要和多个条件关联,就需要添加多个Synchronized锁,而ReentrantLock只需要调用new Condition()犯法即可- Synchronized优势:
不会产生死锁:
Synchronized在Jvm层面实现,在代码出现异常时,Jvm会自动释放锁,不会产生死锁
Lock不行,Lock是基于代码实现的,要保证锁一定会被释放,就必须将锁放到finally()中,且必须保证lock()和unlock()是成对出现的
4.公平锁/非公平锁
公平锁:
多个线程按照申请锁的顺序来获取锁
ReentrantLock(true)公平锁,基于AQS来实现线程调度非公平锁:
多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程先获取锁
Synchronized为非公平锁
ReentrantLock,通过构造函数指定锁手否是公平锁,默认是非公平锁非公平锁的优点:
1) 非公平锁的吞吐量比公平锁大非公平锁的缺点:
1)线程的优先级反转
2)线程饥饿
5.乐观锁/悲观锁
悲观锁:
悲观锁认为对于同一个事务的并发操作,一定是会发生修改的,哪怕是没有发生修改,因此对于同一个数据的并发操作,悲观锁采用加锁的形式
悲观锁在Java中的使用,就是各种锁的使用乐观锁:
乐观锁认为对同一个事务的并发操作,是不会发生修改的,在更新数据的时候,会采用尝试更新的方式,不断尝试的方式更新数据
乐观锁在Java中使用,就是无锁编程,如CAS算法
6.独享锁/共享锁
独享锁:
独享锁指该锁一次只能被一个线程持有
ReentrantLock、Synchronized是独享锁共享锁:
共享锁指锁被多个线程持有
ReentrantReadWriteLock其读锁是共享锁,写锁是独享锁
共享锁的并发是非常高的
7.互斥锁/读写锁
独享锁/共享锁是一种广义上的说法,互斥锁/读写锁就是具体实现,
ReentrantLock是互斥锁
ReentrantReadWriteLock是读写锁
8.可重入锁
- 可重入锁又名递归锁,是指同一个线程在外层获取锁的时候,再次进入内层方法会自动获取锁。
ReentrantLock是一个可重入锁,Synchronized在优化后也算可重入锁(偏向锁)
9.分段锁
- 分段锁是一种设计,并不是锁
对于ConcurrentHashMap而言,其通过分段锁的形式实现高效并发操作,
ConcurrentHashMap中的分段锁称为Segment,它类似于HashMap的结构,内部拥有一个Entry数组,数组中的每个元素是一个链表,同时又是一个ReentrantLock(Seggment集成了ReentrantLock)
当put元素时,不对整个map加锁,先通过HashCode直到它放在哪一个分段中,然后对这个分段加锁,所以多线程中,只要不在一个分段,就实现了真正的并行插入
10.自旋锁
- 尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗