锁是用来控制多个线程访问资源的方式。
在Lock接口出现之前,java程序是靠synchronize关键字实现锁功能的,它是显式的获取锁和释放锁,拥有了获取锁和释放锁的可操作性,可中断以及延时获取锁等多种synchronize所不具备的特性。
Lock接口的实现都是通过自定义了一个队列同步器(AbstractQueneSynchronizer)的子类来实现的。队列同步器使用了一个int成员变量(volatile修饰)来表示同步状态,内置的FIFFO队列来完成资源获取线程的排队工作。可以这样理解,锁是面向使用者的,同步器面向锁的实现者。详细介绍请看下节
目录
一、重入锁
它支持对资源的重复加锁,除此之外,该锁还支持获取锁时的公平和非公平性的选择(synchronize隐式支持可重入)。默认非公平锁,性能好一点。
实现是 通过对同步状态的加1来重新获取锁,减一释放锁
二、读写锁
维护了一对锁,有读锁和写锁。读锁之间是可共享,写写、读写是排他锁。状态维护是通过一个切割的变量维护(高16读,低16位写)
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
出现锁降级(写锁降为读锁)
三、condition 接口
ConditionObject是AQS的内部类,
/**
* Returns a {@link Condition} instance for use with this
* {@link Lock} instance.
*
* <p>The returned {@link Condition} instance supports the same
* usages as do the {@link Object} monitor methods ({@link
* Object#wait() wait}, {@link Object#notify notify}, and {@link
* Object#notifyAll notifyAll}) when used with the built-in
* monitor lock.
*
* <ul>
*
* <li>If this lock is not held when any of the {@link Condition}
* {@linkplain Condition#await() waiting} or {@linkplain
* Condition#signal signalling} methods are called, then an {@link
* IllegalMonitorStateException} is thrown.
*
* <li>When the condition {@linkplain Condition#await() waiting}
* methods are called the lock is released and, before they
* return, the lock is reacquired and the lock hold count restored
* to what it was when the method was called.
*
* <li>If a thread is {@linkplain Thread#interrupt interrupted}
* while waiting then the wait will terminate, an {@link
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
* <li> Waiting threads are signalled in FIFO order.
*
* <li>The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
* acquiring the lock, which is in the default case not specified,
* but for <em>fair</em> locks favors those threads that have been
* waiting the longest.
*
* </ul>
*
* @return the Condition object
*/
public Condition newCondition() {
return sync.newCondition();
}
public class ConditionObject implements Condition, java.io.Serializable {}
包含一个等待队列和首尾节点
await() 将当前线程构造成一个节点加入到等待队列,并释放同步状态
signal() 唤醒等待队列。
四、偏向锁/轻量级锁/重量级锁
https://blog.csdn.net/u010648018/article/details/79750608
这是jdk1.6中对Synchronized锁做的优化
偏向锁、轻量级锁、重量级锁的优缺点
1.偏向锁是为了避免某个线程反复获得/释放同一把锁时的性能消耗,如果仍然是同个线程去获得这个锁,尝试偏向锁时会直接进入同步块,不需要再次获得锁。
2.而轻量级锁和自旋锁都是为了避免直接调用操作系统层面的互斥操作,因为挂起线程是一个很耗资源的操作。
为了尽量避免使用重量级锁(操作系统层面的互斥),首先会尝试轻量级锁,轻量级锁会尝试使用CAS操作来获得锁,如果轻量级锁获得失败,说明存在竞争。但是也许很快就能获得锁,就会尝试自旋锁,将线程做几个空循环,每次循环时都不断尝试获得锁。如果自旋锁也失败,那么只能升级成重量级锁。
偏向锁,轻量级锁都是乐观锁。
五、其它锁分类
1.乐观锁/悲观锁
乐观锁/悲观锁不是指具体类型的锁,而是看待并发的角度。
悲观锁认为存在很多并发更新操作,采取加锁操作,如果不加锁一定会有问题
乐观锁认为不存在很多的并发更新操作,不需要加锁。数据库中乐观锁的实现一般采用版本号,Java中可使用CAS实现乐观锁。
2.分段锁
分段锁是一种锁的设计,并不是一种具体的锁。对于ConcuttentHashMap就是通过分段锁实现高效的并发操作。
3. 自旋锁(是乐观锁)
自旋锁是指尝试获取锁的线程不会阻塞,而是采用循环的方式尝试获取锁。好处是减少上下文切换,缺点是一直占用CPU资源。