AQS底层解析及应用 | 您所在的位置:网站首页 › 48线程应用 › AQS底层解析及应用 |
目录 AQS概述 操作系统层面 线程间通信方式 互斥锁与条件变量 C++原子指令层面 CAS LockSupport Java层面 CLH锁 AQS 应用层面 AQS概述 操作系统层面:mutex+conditionC++原子指令:CAS+LockSupportJava源码层面:CLH锁→AQS应用层面:ReentrantLock,Semaphore,CountDownLatch,CyclicBarrier 操作系统层面 线程间通信方式 互斥锁(mutex)读写锁(rwlock)自旋锁(spin_lock)条件变量(condition) 互斥锁与条件变量 互斥锁:失败后释放CPU条件变量:一种相对复杂的线程同步方法,允许线程睡眠,直到满足某种条件,当满足条件时,可以向该线程发出信号,通知唤醒,条件变量是用来等待线程而不是上锁的条件变量通常和互斥锁一起使用,互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足 两个线程利用条件变量及互斥锁实现同步: 一个线程利用条件变量实现等待,同时释放锁;一个线程获取锁后利用该条件变量唤醒等待的线程 C++原子指令层面 CAS一种乐观锁的实现方式,比较当前工作内存中的值和主内存中的值,如果相同则执行操作,否则继续重新判断,3个操作数:内存值V,旧的预期值A,要修改的更新值B 底层一般是是调用CompareAndSwapInt()方法,通常会和Volatile配合使用,保证可见和有序 LockSupport一个线程阻塞工具类,所有的方法都是静态方法,在初始化的时候都是通过Unsafe去获得他们的内存地址,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。 LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit)。permit只有两个值1和0,默认是0。并且许可的累加上限是1。 permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park()方法会被唤醒,然后会将permit再次设置为0并返回。 Java层面Java在jdk1.5版本之后的 java.util.concurrent包中引入了各类同步器,而其核心就是AQS,AQS的核心数据结构是一种名为CLH 锁的变体 CLH锁传统的CLH锁是对自旋锁的一种改进: 将线程组织成一个队列,防止线程饥饿锁状态去中心化,让每个线程在不同状态变量中自旋,减少cpu开销CLH 锁是一种隐式的链表队列,没有显式的维护前驱或后继指针。因为每个等待获取锁的线程只需要轮询前一个节点的状态即可,而不需要遍历整个队列。在这种情况下,只需要使用一个局部变量保存前驱节点,而不需要显式的维护前驱或后继指针。 特点: 性能优异公平锁,先入队先得到锁实现简单,易于理解扩展性强缺点: 自旋,消耗cpu需要改造,否则性能较为单一 AQSAQS(AbstractQueuedSynchronizer)抽象队列同步器,即将暂时获取不到锁的线程加入到队列中,底层是基于CLH锁的变体,对CLH锁上述缺点进行改造: AQS 将自旋操作改为阻塞线程操作改进主要包括三方面: 扩展每个节点的状态显式的维护前驱节点和后继节点诸如出队节点显式设为 null 等辅助 GC 的优化 应用层面应用 ReentrantLock Semaphore CountDownLatch CyclicBarrier 功能 轮询、超时、中断、公平和非公平锁等 用来控制同时访问特定资源的线程数量 允许 count 个线程阻塞在一个地方,直至所有线程的任务都执行完毕 让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门 实现原理 state变量 permits(可以大于1)+阻塞队列 设置初始state值为count count 的初始值为 parties 属性的初始化值 涉及AQS方法 tryAcquire() CAS tryReleaseShared()AQS判断state ReentrantLock+Condition |
CopyRight 2018-2019 实验室设备网 版权所有 |