说说JAVA中有哪些锁?
一、公平锁和非公平锁
公平锁:多个线程按照申请锁的顺序来获取锁
非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁。这会造成优先级反转或者锁饥饿现象。
在Java中,ReentrantLock可通过构造函数至指定是否是公平锁,默认是非公平锁synchronized默认是非公平锁并且不能变为公平锁
二、独享锁和共享锁
独享锁:一个锁只能被一个线程所持有共享锁:一个锁可被多个线程持有
在Java中,ReentrantLock是独享锁
ReadWriteLock中,读锁是共享锁,写锁是独享锁,即,读写,写读,写写是互斥的
Synchronized方法是独享锁
三、互斥锁和读写锁
互斥锁和读写锁是共享锁和独享锁的具体实现
四、乐观锁和悲观锁
不是具体的锁,是指看待并发同步的角度
悲观锁:对于同一数据的并发操作,悲观锁认为这是一定会修改数据的,因此会采取加锁的方式实现同步。
乐观锁:对于同一数据的并发操作,乐观锁认为这不一定会修改数据,因此在更新数据时,会采取不断尝试更新的操作。
在Java中,悲观锁是指利用各种锁机制;而乐观锁是指无锁编程,如CAS算法,典型的是原子类,通过CAS自旋实现原子操作的更新
五、分段锁
不是指具体的一种锁,而是一种锁的设计。
分段锁的设计目的是细化锁的操作,例如当操作不需要更新整个数组时,仅针对数组中的一个元素进行更新时,我们仅给该元素加锁即可。
ConcurrentHashMap就是利用分段锁的形式实现高效地并发操作:
ConcurrentHashMap和hashMap一样,有一个Entry数组,数组中的每个元素是一个链表,也是一个Segment(分段锁)。
当需要put元素时,不是先对整个hashMap加锁(线程安全的hashTable是整个加锁),而是通过hashCode知道它要放在哪一个分段中,然后对这个分段进行加锁,所以当有多个线程put时,只要不是放在同一个分段中,就不会产生同步阻塞现象。
在统计size时,即获取ConcurrentHashMap信息时,就需要获取所有分段锁才能统计。
六、偏向锁,轻量级锁,重量级锁
在Java5中,可以通过锁升级机制实现高效地Synchronized方法,这三种锁是指Synchronized锁的状态,通过对象监视器在对象头中的字段来表明。
偏向锁:指一段同步代码一直被一个线程所访问,那么该线程就会自动获取这个锁,以降低获取锁的代价。
轻量级锁:当前锁是偏向锁并且被另一个线程访问时,偏向锁会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞其他线程。
重量级锁:当前锁时轻量级锁,另一个线程自旋到一定次数的时候还没获取到该锁时,轻量级锁就会升级为重量级锁,会阻塞其他线程。
七、自旋锁
指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁
八、可重入锁(递归锁)
指在同一线程在外层方法获取锁的时候,进入内层方法时会自动获取锁