管程是管理共享变量和对共享变量 *** 作的过程;上文讲解了synchronized在jvm层面实现了MESA管程模型,本文阐述AQS在java层面实现MESA管程模型
AQS--AbstractQueuedSynchronizerAQS是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一样。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)