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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值