欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java8 Lock锁详解(AQS,CAS)

程序员文章站 2022-03-08 18:59:40
...

什么是Lock锁?

Lock锁提供了的比synchronized关键字更加灵活的锁操作,是代码层面的锁操作。
Java8 Lock锁详解(AQS,CAS)

为什么要使用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();
        }
    }
}

类功能分析

Java8 Lock锁详解(AQS,CAS)
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的内部类。来实现其特有功能。
Java8 Lock锁详解(AQS,CAS)
例如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;
}