2026/4/19 22:31:29
网站建设
项目流程
设计好看的企业网站,长春网站制作价格,互联网排名前100的公司,网页设计与制作培训班哪家好接下来将从实现角度分析同步器是如何完成线程同步的#xff0c;主要包括#xff1a;同步队列、独占式同步状态获取与释放、共享式同步状态获取与释放以及超时获取同步状态等同步器的核心数据结构与模板方法。一、同步队列同步器依赖内部的同步队列#xff08;一个FIFO双向队…接下来将从实现角度分析同步器是如何完成线程同步的主要包括同步队列、独占式同步状态获取与释放、共享式同步状态获取与释放以及超时获取同步状态等同步器的核心数据结构与模板方法。一、同步队列同步器依赖内部的同步队列一个FIFO双向队列来完成同步状态的管理当前线程获取同步状态其实就是锁失败时同步器会将当前线程以及等待状态等信息构造成为一个节点Node并将其加入同步队列同时会阻塞当前线程当同步状态释放时会把首节点中的线程唤醒使其再次尝试获取同步状态。同步队列中的节点Node用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点节点的属性类型与名称以及描述等信息。节点是构成同步队列的基础同步器拥有首节点head和尾节点tail没有成功获取同步状态的线程将会成为节点加入该队列的尾部。由上图可知同步器包含了两个节点类型的引用一个指向头节点而另一个指向尾节点。试想一下当一个线程成功地获取了同步状态或者锁其他线程将无法获取到同步状态转而被构造成为节点并加入到同步队列中而这个加入队列的过程必须要保证线程安全因此同步器提供了一个基于CAS的设置尾节点的方法compareAndSetTail(Node expect,Nodeupdate)它需要传递当前线程“认为”的尾节点和当前节点只有设置成功后当前节点才正式与之前的尾节点建立关联。同步队列遵循FIFO首节点是获取同步状态成功的节点首节点中的线程在释放同步状态时将会唤醒它的后继节点而后继节点将会在获取同步状态成功时将自己设置为首节点。设置首节点是通过获取同步状态成功的线程来完成的由于只有一个线程能够成功获取到同步状态因此设置头节点的方法并不需要使用CAS来保证。二、独占式同步状态获取与释放通过调用同步器的acquire(int arg)方法可以获取同步状态该方法对中断不敏感也就是由于线程获取同步状态失败后进入同步队列中后续对线程进行中断操作时线程不会从同步队列中移出同步器的acquire方法。public final void acquire(int arg) { if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }上述代码主要完成了同步状态获取、节点构造、加入同步队列以及在同步队列中自旋等待的相关工作其主要逻辑是首先调用自定义同步器实现的tryAcquire(int arg)方法该方法保证线程安全的获取同步状态如果同步状态获取失败则构造同步节点独占式Node.EXCLUSIVE同一时刻只能有一个线程成功获取同步状态并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部最后调用acquireQueued(Node node,int arg)方法使得该节点以“死循环”的方式获取同步状态死循环就是为了实现自旋。如果获取不到则阻塞节点中的线程而被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。三、共享式同步状态获取与释放共享式获取与独占式获取最主要的区别在于同一时刻能否有多个线程同时获取到同步状态。以文件的读写为例如果一个程序在对文件进行读操作那么这一时刻对于该文件的写操作均被阻塞而读操作能够同时进行。写操作要求对资源的独占式访问而读操作可以是共享式访问。通过调用同步器的acquireShared(int arg)方法可以共享式地获取同步状态在acquireShared(int arg)方法中同步器调用tryAcquireShared(int arg)方法尝试获取同步状态该方法返回值为int类型当返回值大于等于0时表示能够获取到同步状态。因此在共享式获取的自旋过程中成功获取到同步状态并退出自旋的条件就是该方法返回值大于等于0。与独占式一样共享式获取也需要释放同步状态通过调用releaseShared(int arg)方法可以释放同步状态。该方法在释放同步状态之后将会唤醒后续处于等待状态的节点。对于能够支持多个线程同时访问的并发组件比如Semaphore它和独占式主要区别在于tryReleaseShared(int arg)方法必须确保同步状态或者资源数线程安全释放一般是通过循环和CAS来保证的因为释放同步状态的操作会同时来自多个线程。四、独占式超时获取同步状态通过调用同步器的doAcquireNanos(int arg,long nanosTimeout)方法可以超时获取同步状态即在指定的时间段内获取同步状态如果获取到同步状态则返回true否则返回false。该方法提供了传统Java同步操作比如synchronized关键字所不具备的特性。在分析该方法的实现前先介绍一下响应中断的同步状态获取过程。在Java 5之前当一个线程获取不到锁而被阻塞在synchronized之外时对该线程进行中断操作此时该线程的中断标志位会被修改但线程依旧会阻塞在synchronized上等待着获取锁。在Java 5中同步器提供了acquireInterruptibly(int arg)方法这个方法在等待获取同步状态时如果当前线程被中断会立刻返回并抛出InterruptedException。超时获取同步状态过程可以被视作响应中断获取同步状态过程的“增强版”doAcquireNanos(int arg,long nanosTimeout)方法在支持响应中断的基础上增加了超时获取的特性。针对超时获取主要需要计算出需要睡眠的时间间隔nanosTimeout为了防止过早通知。nanosTimeout计算公式为nanosTimeout-now-lastTime其中now为当前唤醒时间lastTime为上次唤醒时间如果nanosTimeout大于0则表示超时时间未到需要继续睡眠nanosTimeout纳秒反之表示已经超时。