java.util.concurrent.locks.AbstractQueuedSynchronizer构造同步类

AbstractQueuedSynchronizer是一个同步架构, Java中有很多同步类是由这个架构实现,比如FutureTask、ReentrantLock等。为了使用这个类实现一个同步架构,需要将其定义为一个帮助子类,需要适当地重新定义以下方法,这是通过使用getState 、 setState、或compareAndSetState 方法来检查和/或修改同步状态来实现的:

您也可以查找从 AbstractOwnableSynchronizer 继承的方法,用于跟踪拥有独占同步器的线程。鼓励使用这些方法,这允许监控和诊断工具来帮助用户确定哪个线程保持锁。

即使此类基于内部的某个 FIFO 队列,它也无法强行实施 FIFO 获取策略。独占同步的核心采用以下形式:

 Acquire:
     while (!tryAcquire(arg)) {
        enqueue thread if it is not already queued;
        possibly block current thread;
     }

 Release:
     if (tryRelease(arg))
        unblock the first queued thread;
 
(共享模式与此类似,但可能涉及级联信号。)

因为要在加入队列之前检查线程的获取状况,所以新获取的线程可能闯入 其他被阻塞的和已加入队列的线程之前。不过如果需要,可以内部调用一个或多个检查方法,通过定义 tryAcquire 和/或 tryAcquireShared 来禁用闯入。特别是 getFirstQueuedThread() 没有返回当前线程的时候,严格的 FIFO 锁定可以定义 tryAcquire 立即返回 false。只有 hasQueuedThreads() 返回 true 并且 getFirstQueuedThread 不是当前线程时,更好的非严格公平的版本才可能会立即返回 false;如果 getFirstQueuedThread 不为 null 并且不是当前线程,则产生的结果相同。出现进一步的变体也是有可能的。

对于默认闯入(也称为 greedyrenouncementconvoy-avoidance)策略,吞吐量和可伸缩性通常是最高的。尽管无法保证这是公平的或是无偏向的,但允许更早加入队列的线程先于更迟加入队列的线程再次争用资源,并且相对于传入的线程,每个参与再争用的线程都有平等的成功机会。此外,尽管从一般意义上说,获取并非“自旋”,它们可以在阻塞之前对用其他计算所使用的 tryAcquire 执行多次调用。在只保持独占同步时,这为自旋提供了最大的好处,但不是这种情况时,也不会带来最大的负担。如果需要这样做,那么可以使用“快速路径”检查来先行调用 acquire 方法,以这种方式扩充这一点,如果可能不需要争用同步器,则只能通过预先检查 hasContended() 和/或 hasQueuedThreads() 来确认这一点。

通过特殊化其同步器的使用范围,此类为部分同步化提供了一个有效且可伸缩的基础,同步器可以依赖于 int 型的 state、acquire 和 release 参数,以及一个内部的 FIFO 等待队列。这些还不够的时候,可以使用 atomic 类、自己的定制 Queue 类和 LockSupport 阻塞支持,从更低级别构建同步器。 


使用示例

以下是一个非再进入的互斥锁类,它使用值 0 表示未锁定状态,使用 1 表示锁定状态。当非重入锁定不严格地需要当前拥有者线程的记录时,此类使得使用监视器更加方便。它还支持一些条件并公开了一个检测方法:

 class Mutex implements Lock, java.io.Serializable {

    // Our internal helper class
    private static class Sync extends AbstractQueuedSynchronizer {
      // Report whether in locked state
      protected boolean isHeldExclusively() { 
        return getState() == 1; 
      }

      // Acquire the lock if state is zero
      public boolean tryAcquire(int acquires) {
        assert acquires == 1; // Otherwise unused
       if (compareAndSetState(0, 1)) {
         setExclusiveOwnerThread(Thread.currentThread());
         return true;
       }
       return false;
      }

      // Release the lock by setting state to zero
      protected boolean tryRelease(int releases) {
        assert releases == 1; // Otherwise unused
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
      }
       
      // Provide a Condition
      Condition newCondition() { return new ConditionObject(); }

      // Deserialize properly
      private void readObject(ObjectInputStream s) 
        throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
      }
    }

    // The sync object does all the hard work. We just forward to it.
    private final Sync sync = new Sync();

    public void lock()                { sync.acquire(1); }
    public boolean tryLock()          { return sync.tryAcquire(1); }
    public void unlock()              { sync.release(1); }
    public Condition newCondition()   { return sync.newCondition(); }
    public boolean isLocked()         { return sync.isHeldExclusively(); }
    public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
    public void lockInterruptibly() throws InterruptedException { 
      sync.acquireInterruptibly(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit) 
        throws InterruptedException {
      return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
 }
看公有方法,是我们所熟悉的几个同步类的方法,他们封装了sync的方法。

1. lock(), 这个方法调用了sync.acquire(1)获取了独占锁acquire的定义如下

public final void acquire(int arg)
以独占模式获取对象,忽略中断。通过至少调用一次 tryAcquire(int) 来实现此方法,并在成功时返回。否则在成功之前,一直调用 tryAcquire(int) 将线程加入队列,线程可能重复被阻塞或不被阻塞。可以使用此方法来实现 Lock.lock() 方法。

参数:
arg - acquire 参数。此值被传送给 tryAcquire(int),但它是不间断的,并且可以表示任何内容。

     
     

我们看到acquire是通过调用tryAcquire(int)来实现的,直到成功返回时结束,因此我们无需要自定义这个方法就可用它来实现lock。

 

2.tryLock()是由sync.tryAcquire(1)来实现

 

3.unlock()由Release(int)来实现,我们看下release的定义

public final boolean release(int arg)
以独占模式释放对象。如果 tryRelease(int) 返回 true,则通过消除一个或多个线程的阻塞来实现此方法。可以使用此方法来实现 Lock.unlock() 方法

参数:
arg - release 参数。此值被传送给 tryRelease(int),但它是不间断的,并且可以表示任何内容。
返回:
tryRelease(int) 返回的值
这个类似与tryAcquire(int)
剩下方法分别调用了AQS的其他方法,不再一一介绍,下面看下tryAcquire(int)方法的实现
 // Acquire the lock if state is zero
      public boolean tryAcquire(int acquires) {
        assert acquires == 1; // Otherwise unused
       if (compareAndSetState(0, 1)) {
         setExclusiveOwnerThread(Thread.currentThread());
         return true;
       }
       return false;
      }

我们看到首先去compareAndSetState(0, 1),如果满足,则setExclusiveOwnerThread(Thread.currentThread()),这个方法是AQS的父类
AbstractOwnableSynchronizer的方法,这是个作为监控作用的类。之后成功以后,返回true,否则返回false。
总结
当类库提供的同步类不能满足我们的要求的时候,或者我们需要关注类实现细节的时候,我们需要对AQS类有所了解,这是个同步框架,用来实现基于状态的同步类,其中最关键的方法是getState()、setState()、compareAndSetState(),这三个方法由架构提供,我们需要调用这三个方法来实现tryAcquire等方法来建立我们自定义的同步类,当然在一般情况下类库提供的同步类已足够我们所使用。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值