Java8 Lock锁详解(AQS,CAS)
什么是Lock锁?
Lock锁提供了的比synchronized关键字更加灵活的锁操作,是代码层面的锁操作。
为什么要使用Lock锁?
Lock锁和synchronized关键字的对比
类型 | synchronized关键字 | Lock锁 |
---|---|---|
获取锁 | 无超时时间,未获取到则阻塞等待(占用cpu资源),且无法被中断 | 非阻塞,可以被中断,未获取到则排队,中断,可以自定义超时时间 |
共享锁 | 不支持 | 读写锁ReadWriteLock支持 |
释放锁 | 必须在当前代码块中,因为synchronized是以{}来锁住共享资源的 | 可以在任意位置释放锁(其他方法或者其他类) |
概念普及
公平锁:保证每个线程都能获取到锁(排队先进来的先获取到锁),能够避免线程饥饿
非公平锁:谁抢到就是谁的,抢不到就排队
悲观锁:每次更新和读取都加锁,对于读多写少这种场景不利,适用于于写多读少的场景
乐观锁:更新失败则重试,适用一个标记位来控制,适用于多读的场景。
可重入锁:同一线程多次进入无需重新获取锁
共享锁(读锁):一个锁可以同时被多个线程拥有,ReentrantReadWriteLock的读锁为共享锁,写锁为排他锁
排他锁(写锁):独占锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁,ReentrantLock就是排他锁。
如何使用?
public class LockTest {
// 这个必须放在成员变量或者作为方法参数进行传递。具体原因见后面代码分析
private final Lock lock = new ReentrantLock();
// 共享资源a
private int a = 0;
public static void main(String[] args) throws InterruptedException {
LockTest lockTest = new LockTest();
for (int i = 0; i < 10; ++i) {
// 模拟10个线程对 a进行++
new Thread(() -> {
for (int j = 0; j < 10000; ++j) {
lockTest.testLock();
}
}).start();
}
// 防止因为主线程结束,导致程序运行失败
Thread.sleep(5000);
System.out.println(lockTest.a);
}
public void testLock() {
try {
// 这块加锁也应该放到for循环外面。放里面会有性能问题。每+1就释放锁,
// 正常用该是+n然后等待cpu调度,然后释放锁
lock.lock();
++a;
} finally {
lock.unlock();
}
}
}
类功能分析
lock所有相关的代码都在java.utils.concurrent.locks这个包下。其中Lock和ReadWriteLock接口类是对外暴露的api
涉及到的设计模式:模板设计模式,组合模式
AbstractOwnableSynchronizer:AbstractQueuedSynchronizer的父类,仅仅是为了让子类能够设置和获取独占线程。
public abstract class AbstractOwnableSynchronizer {
protected AbstractOwnableSynchronizer() { }
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
AbstractQueuedSynchronizer:提供了对Lock接口中,lock,tryLock(),unlock(),等方法的通用实现和通用方法(模板设计模式),相当于是一个工具类。
对于每一种锁,lock,tryLock,unlock又有其不同的实现。所以对于每一种Lock锁的实现,都会定义一个Sync的内部类。来实现其特有功能。
例如ReentrantLock类的实现如下。ReentrantLock的lock,tryLock等方法都是调用其内部类Sync的方法。
public class ReentrantLock implements Lock {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
// 由两个子类(FairSync和NonfairSync)来实现。
// 非公平锁直接使用cas抢(设置锁标志位为1),抢不到就按照正常的流程来(排队,先进先出(先等待的先获取到锁))
// 公平锁走正常流程(排队,先进先出)
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {
// ...
}
protected final boolean tryRelease(int releases) {
// ...
}
protected final boolean isHeldExclusively() {
// ...
}
}
public void lock() {
// 调用内部类Sync的实现
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
// 调用内部类Sync的实现
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
// 调用内部类Sync的实现
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// 调用内部类Sync的实现
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
// 调用内部类Sync的实现
sync.release(1);
}
}
从上面的实现可以看出,如果Sync类是public的。我们其实可以直接使用Sync类来进行并发编程。使用Lock接口来包装一层只是让我们使用更加方便而已。
LockSupport类:工具类,方法都是public static的。用于挂起,恢复一个线程。
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
// 恢复线程
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
// 挂起线程
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
ReentrantLock源码剖析
private final Lock lock = new ReentrantLock();
lock加锁
// 默认为非公平锁
public ReentrantLock() {
// NonfairSync是Sync的一个实现类,实现了他的lock方法
sync = new NonfairSync();
}
ReentrantLock#lock方法调用的是其内部类Sync的lock方法
public void lock() {
// 由于默认为非公平锁,所有这里调用的是NonfairSync的lock方法
sync.lock();
}
NonfairSync类
static final class NonfairSync extends Sync {
final void lock() {
// 使用cas加锁(如果state等于0则设置为1返回true,否则返回false),
// 只有没有任何线程持有锁时,这里才会返回true
if (compareAndSetState(0, 1))
// 加锁成功则设置独占线程为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 加锁失败则调用AbstractQueuedSynchronizer类的acquire方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
// 调用父类Sync的nonfairTryAcquire方法
return nonfairTryAcquire(acquires);
}
}
// AbstractQueuedSynchronizer类的acquire方法
public final void acquire(int arg) {
// 调用NonfairSync类的tryAcquire方法
if (!tryAcquire(arg) &&
// 如果tryAcquire返回false,加锁失败则进行排队
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 两个都是false。则当前线程请求锁失败,中断自己。
selfInterrupt();
}
// Sync类
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前锁状态
int c = getState();
if (c == 0) {
// 为0则表示锁是空闲状态,则加锁
if (compareAndSetState(0, acquires)) {
// 设置独占线程为自己
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 如果自己就是独占线程,则只是将锁的状态标志位+1
// acquires外部传的是1,一般也不会出现其他数字
// 这里就是锁的可重入实现。只是增加状态值,释放锁时再减状态值。
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置状态为 state += 1,这里不会出现线程安全问题,因为锁被自己占有
// 所以直接调用setState方法,而不是调用compareAndSetState(c, acquires)方法。
setState(nextc);
return true;
}
return false;
}
}
unlock解锁
lock.unlock();
解锁同样调用的是Sync的实现,数字1代表锁的标志位state - 1,因为之前在加锁时,会将state + 1;
public void unlock() {
sync.release(1);
}
Sync类中并没有实现release方法,因为所有锁的释放操作其实都是一样的,所以这里调用的是父类AbstractQueuedSynchronizer的release方法
AbstractQueuedSynchronizer#release
// 释放锁
public final boolean release(int arg) {
// 调用Sync类的tryRelease方法
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 如果有线程在等待锁释放,则将该线程恢复
unparkSuccessor(h);
// 没有线程在等待资源,则直接返回true
return true;
}
return false;
}
// 返回当前锁的状态(true空闲,false,表示当前线程多次获取锁,是重入状态)
protected final boolean tryRelease(int releases) {
// 锁的状态标志位-1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
// 设置独占线程为空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
上一篇: Java多线程机制
下一篇: 淘宝喵糖什么时间有翻倍 淘宝喵糖几点翻倍
推荐阅读
-
对python多线程中互斥锁Threading.Lock的简单应用详解
-
对python多线程中Lock()与RLock()锁详解
-
Java 开发中的Lock锁和Synchronized详解
-
Java中的锁原理、锁优化、CAS、AQS详解!
-
ORACLE基础之oracle锁(oracle lock mode)详解
-
解读可重入锁——ReentrantLock&AQS,java8
-
JAVA锁体系、CAS、AQS详细讲解
-
Java多线程复习(二):UnSafe与CAS、AQS与锁、CountDownLatch、CyclicBarrier、Semaphore
-
深入剖析Java中的锁【原理、锁优化、CAS、AQS】
-
Mysql 死锁过程及案例详解之记录锁与间隔锁Record Lock Gap Lock