当前位置: 首页 > news >正文

哈尔滨精品网站制作专业网站优化公司报价

哈尔滨精品网站制作,专业网站优化公司报价,做标书要不要做网站,专利协会网站建设方案引言 AQS 是AbstractQuenedSynchronizer 的缩写#xff0c;抽象的队列式同步器#xff0c;它是除了java自带的synchronized关键字之外的锁机制。是 JUC 下的重要组件。 相关产物有#xff1a;ReentrantLock、CountDownLatch、Semaphore、ReadWriteLock等。 一、AQS的设计…引言 AQS 是AbstractQuenedSynchronizer 的缩写抽象的队列式同步器它是除了java自带的synchronized关键字之外的锁机制。是 JUC 下的重要组件。 相关产物有ReentrantLock、CountDownLatch、Semaphore、ReadWriteLock等。 一、AQS的设计思想 AbstractQuenedSynchronizer 维护了一个 volatile int state 变量代表共享资源。 若state 是0代表资源空闲当前线程将 0 改为 1表示上锁当前线程置为工作线程 若state不为0代表资源占用当前线程依然会 acquire() 一个资源如果恰好是当前的工作线程那么state 累加以此描述“重入性”如果当前线程并不是工作线程则会被安置在一个由AQS维护的资源等待队列。 AQS队列会让第一个线程Node自旋获取资源而后面的线程则通过 LockSupport.park(this) 方法将线程置为 WAITING 状态等待被唤醒。 如果第一个线程获取到了资源那么就将它设置为队列的 head 节点原 head 就会被移出队列。 AQS的设计中用到了模板方法模式不同的资源共享机制如互斥或共享可以由子类自定义实现 Exclusive如ReentrantLock、 Share如信号量、闭锁、读写锁等。 AQS 的另一个特点是自旋CAS。 在请求资源和入列等操作中经常会看到 for(;;) 、compareAndSetState、compareAndSetTail等操作这与synchronized的实现有很大区别。 通过比较并设置的方式可以有效提高资源获取的效率但同时也会消耗额外的CPU资源。 二、两种资源访问策略的代表 在AQS中维护了一个 Node 节点它有两种等待模式同时也表示资源的两种不同的访问策略 /** Marker to indicate a node is waiting in shared mode */ static final Node SHARED new Node(); /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE null; 由此衍生出两类不同的子类实现第一类是以ReentrantLock为代表的互斥锁它在语义上与synchronized实现了相同的互斥性和可重入性另一类是以闭锁CountDownLatch 为代表的线程同步工具。 2.1 ReentrantLock 首先 AQS 中的 state 为 0. A 线程在执行 lock() 方法后以独占方式 CAS state 为 1。AQS 会记录 A 线程为当前的独占线程其他线程如果再尝试获取资源就会进入等待队列直到 A 线程调用 unlock() 方法释放了资源即 state 回归 0 状态。 在“重入性”方面如果A线程第二次尝试取锁state 会累加。也就是说上锁的次数一定等于释放锁的次数。 2.2 CountDownLatch CountDownLatch 翻译为 “闭锁”或“门闩”这是一种非常好用的同步工具可以延迟线程的进度直到终止状态。 与 ReentrantLock 不同的是在构造 CountDownLatch 对象的时候会先设定一个 state 大小 CountDownLatch latch new CountDownLatch(3); 这个 3 就是 state 变量的初始值然后线程使用 countDown() 方法递减这个计数直到 state 0放行所有 waiting 中的线程。 CountDownLatch 维护的 state 表示的是事件数量当指定数量的事件执行完毕后就会 unpark() 主调线程继续后续动作。 在使用CountDownLatch时有一个误区是state 的值就代表了线程的数量认为我 state 3 就需要 3 个线程去执行任务其实state 10 也依然可以使用 一个线程去执行关键要区分事件与并行任务的概念。 三、ReentrantLock 源码 作为补充 synchronized 的锁机制ReentrantLock 显示锁的功能非常强大但这里不打算全面分析ReentrantLock的奇技淫巧而是从 lock() 方法出发分析一下 AQS 是如何实现资源的锁定和等待队列的维护的。 3.1 acquire acquire 是 AQS 的顶层入口他表示获取锁资源。 public final void acquire(int arg) {if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt(); } Doug Lea 的代码非常简洁根本没有一句废话就连代码结构也非常精简如果是分析源码的话我们可以尝试去改写一下这个方法让其可读性更强一些 public final void acquire(int arg) {if (!tryAcquire(arg)) {Node newWaiter addWaiter(Node.EXCLUSIVE);boolean needInterrupt acquireQueued(newWaiter, arg);if (needInterrupt) {selfInterrupt();}} } 从方法中的一系列方法名和判断逻辑来看。 尝试获取资源如果成功则直接返回。如果不成功addWaiter 添加一个独占模式的等待者acquireQueued 以排队的方式去获取资源。 3.2 tryAcquire tryAcquire 在 ReentrantLock 中有两种实现分别是 FairSync 中的公平锁实现 NonfairSync 中的非公平锁实现 当然公平与非公平并不是重点就以非公平的实现来看一下 final boolean nonfairTryAcquire(int acquires) {final Thread current Thread.currentThread();int c getState();if (c 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current getExclusiveOwnerThread()) {int nextc c acquires;if (nextc 0) // overflowthrow new Error(Maximum lock count exceeded);setState(nextc);return true;}return false; } 当前线程会 CAS state 0-1或累加重入成功返回true失败返回false。 3.3 addWaiter 在acquire上锁操作失败后会执行这个方法 private Node addWaiter(Node mode) {Node node new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred tail;if (pred ! null) {node.prev pred;if (compareAndSetTail(pred, node)) {pred.next node;return node;}}enq(node);return node; } addWaiter 添加一个等待者它只完成一项工作就是向等待队列中添加一个 Node 1、将当前线程封装为一个队列 Node 2、取得队列的尾节点 tail并CAS 新的节点设置为新的 tail 3、设置新 tail 成功直接返回 4、若设置新 tail 不成功或者干脆原tail 就不存在执行 enq 方法自旋操作以上步骤直到成功。 enq方法是 enqueue 的缩写意思是“使队列化”它就是一个 while-true 如果队列不存在就创建一个队列如果队列已经存在就把 node 放到最后一个 private Node enq(final Node node) {for (;;) {Node t tail;if (t null) { // Must initializeif (compareAndSetHead(new Node()))tail head;} else {node.prev t;if (compareAndSetTail(t, node)) {t.next node;return t;}}} } 这里就用到了自旋操作每次自旋都会获取当前的 tail 节点避免在设置的过程中间被其他线程加塞却又不知道。 刚进入方法的时候肯定需要走初始化的逻辑这会创建一个 空的 Node 节点作为 head所以由此我们也知道AQS 队列中的头结点实际上就是一个没有实际意义的功能型节点里边是没有线程的真正封装了线程的节点是从第二个节点开始。 总体来看addWaiter 完全就是一个 do-while 循环先执行一次 CASTail失败后循环执行CASTail直到成功后返回该 node同时也是新的 tail 节点。 3.4 acquireQueued 在 addWaiter 添加了新的 tail 后需要做哪些事情呢acquireQueued final boolean acquireQueued(final Node node, int arg) {boolean failed true;try {boolean interrupted false;for (;;) {final Node p node.predecessor();if (p head tryAcquire(arg)) {setHead(node);p.next null; // help GCfailed false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt())interrupted true;}} finally {if (failed)cancelAcquire(node);} } 这里需要说明一下该方法的逻辑兼顾了中断的操作如果对中断机制不太了解可以暂时不去理会。 该方法同样是一个 while-true 循环当且仅当当前节点是队列中第二个节点addWaiter中已经很明确AQS队列中的head 节点就是一个空的 Node并且 tryAcquire 成功才会返回。在返回之前仅做了一些队列的维护工作设置新的head 节点。 如果没有“当且仅当”那么执行 park private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted(); } 也就是说除了第二个节点以后的节点都要进行 park即线程切换为 WAITING 状态。 四、AQS acquire 流程 经过了上一节的源码分析我们已经大概清楚了 lock() 方法调用之后发生的事情接下来就需要总结一下 acquire 流程步骤提炼一下 AQS 队列的工作原理 总结 AQS 使用了大量的 CAS 操作避免上锁你在ReentrantLock中看不到一句 synchronized 。 通过CAS 和自旋的配合可以一定程度上提高同步代码的性能。 state 以 volatile 类型修饰可以在多线程之间提供可见性。 ReentrantLock 和 CountDownLatch 对 state 的访问方式分为独占和共享两种本文虽然没有解析 CountDownLatch 的源码但通过上面源码的分析可以想到其大致实现流程。
http://www.zqtcl.cn/news/674183/

相关文章:

  • 企业电子商务网站建设和一般百拓公司做网站怎么样
  • 吉林网站建设司上海什么做网站的公司比较好
  • 吉安市建设规划局网站jsp wordpress
  • 建设银行贵金属网站微信小程序注册后怎么使用
  • 如何做律师网站河南建网站 优帮云
  • 云阳如何做网站网站建设旅游
  • 推荐一个简单的网站制作单位网站服务的建设及维护
  • tp5网站文档归档怎么做网站 信用卡支付接口
  • phpcms 企业网站网站建设中单页代码
  • 坑梓网站建设方案网络编程技术及应用
  • 电子商务网站建设 价格新媒体运营需要具备哪些能力
  • 做生存分析的网站电商网站运营建设的目标
  • 佛山 做网站邮箱官方网站注册
  • 生成flash的网站源码表白二维码制作网站
  • 定做专业营销型网站网站开发应用
  • 万盛建设局官方网站如何用群晖nas做网站
  • 建设装饰网站郑州惠济区建设局网站
  • 网站做标题有用吗网站优化多少钱
  • 婚庆设备租赁网站源码如何进行网站的建设和维护
  • 青岛做网站公wordpress文章付费阅读
  • 小灯具网站建设方案360优化大师
  • 开发公司与物业公司前期合同网站优化的推广
  • 汉堡云虚拟主机aso安卓优化公司
  • 医院 网站建设 新闻营销外包
  • 优秀网站网址郑州无痛人流哪家医院好
  • 备案网站能打开吗大良营销网站建设流程
  • 哪些网站可以做淘宝店招石油网站编辑怎么做
  • 网站出现建设中集团网站建设特点
  • asp网站开发 pdf企业展厅设计公司盛世笔特
  • 怎么创建网站 免费的免费开源的网站系统