java实现管程之AQS以及ReentrantLock

java实现管程之AQS以及ReentrantLock,第1张

java实现管程之AQS以及ReentrantLock 管程

管程是管理共享变量和对共享变量 *** 作的过程;上文讲解了synchronized在jvm层面实现了MESA管程模型,本文阐述AQS在java层面实现MESA管程模型

AQS--AbstractQueuedSynchronizer

AQS是java层面实现管程的同步队列抽象类,抽象类实现管程的同步等待队列和条件等待队列;而加锁和解锁的逻辑由具体的子类实现,只是对外提供模板方法,由程序员自己实现加解锁的逻辑。

AQS特性

阻塞等待队列;共享/独占;公平/非公平;可重入;可中断

AQS内部核心

state变量:是volatile修饰的int类型,能否获取锁就是通过CAS *** 作设置state变量来控制

同步等待队列:CAS尝试修改state相当于竞争锁,获得锁成功则执行业务逻辑,竞争锁失败则进入同步等待队列并调用LockSupport.park()方法阻塞当前线程,等待其他获得锁的线程调用LockSupport.unpark()方法唤醒该线程并出队;同步等待队列是双向链表结构

条件等待队列:Condition,await()/signal()、signalAll(),阻塞唤醒机制来对线程进行入队出队 *** 作,单向链表结构

Condition接口:await()/signal()、signalAll()

两种共享资源:

SHARED-共享,多个线程可以同时执行,如Semaphore/CountDownLatch

EXCLUSIVE-独占,只有一个线程能执行,如ReentrantLock

中断:线程t1调用lockInterruptibly()方法竞争锁,此时锁已经被t2占有,t1竞争锁失败被阻塞,t2执行完了并调用t1.interrupt()中断线程t1,t1获取锁失败会抛出中断异常,最终执行catch逻辑。

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("t1启动...");
            try {
                lock.lockInterruptibly();
                try {
                    log.debug("t1获得了锁");
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("t1等锁的过程中被中断");
            }
        }, "t1");

        lock.lock();
        try {
            log.debug("main线程获得了锁");
            t1.start();
            Thread.sleep(1000);
            t1.interrupt();
            log.debug("线程t1执行中断");
        } finally {
            lock.unlock();
        }
    }
ReentrantLock

ReentrantLock是基于AQS框架实现的一种线程并发访问的同步手段,它的功能类似与synchronized是一种互斥锁,可以保证线程安全。

ReentrantLock与synchronized优缺点和异同点

1、ReentrantLock实现了公平锁和非公平锁,而synchronized是非公平锁(先进后出的栈结构通过单向链表实现)

2、ReentrantLock可以显示的控制加锁和解锁的逻辑,synchronized基于jvm实现隐示自动加解锁

3、synchronized是不可以被中断的,ReentrantLock是可以被中断的(lockInterruptibly())

4、ReentrantLock获得锁的方式灵活多变如:lock(),tryLock(),tryLock(time, unit),lockInterruptibly(),synchronized能修饰方法和代码块。

5、它们都是可重入锁

6、synchronized是基于jvm实现,ReentrantLock是jdk实现的

可重入:thread线程变量记录判断是否同一线程进入,表示可重入

可中断

锁超时:tryLock()--立即超时,tryLock(time, unit)--超时时间

条件变量

ReentrantLock源码执行逻辑跟踪

for (int i = 0; i < 3; i++) {
    Thread thread = new Thread(()->{
        lock.lock();
        try {
            for (int j = 0; j < 10000; j++) {
                sum++;
            }
        } finally {
            lock.unlock();
        }
    });
    thread.start();
}
Thread.sleep(2000);
System.out.println(sum);

竞争锁的逻辑流程 

当t1和t2都入队以后,t0释放锁,将state设置为0,并将AQS的exclusiveOwnerThread属性设置为null,设置waitStatus为0,并调用LockSupport.unpark(t1)唤醒t1线程

t1唤醒后将thread属性设置为null,然后调用tryLock()方法竞争锁,此时state=0,将AQS的head属性设置为node1,并将node0和node1之间的next和prev属性置为null,相当于将node0剔除,至此t1获得锁。

t2获得锁的流程与t1一样。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5686877.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存