zoukankan      html  css  js  c++  java
  • Java并发框架——AQS之怎样使用AQS构建同步器

    AQS的设计思想是通过继承的方式提供一个模板让大家能够非常easy依据不同场景实现一个富有个性化的同步器。同步器的核心是要管理一个共享状态,通过对状态的控制即能够实现不同的锁机制。

    AQS的设计必须考虑把复杂反复且easy出错的队列管理工作统一抽象出来管理,而且要统一控制好流程,而暴露给子类调用的方法主要就是操作共享状态的方法,以此提供对状态的原子性操作。一般子类的同步器中使用AQS提供的getState、setState、compareAndSetState三个方法,前两个为普通的get和set方法,要使用这两个方法必需要保证不存在数据竞争,compareAndSetState方法提供了CAS方式的硬件级别的原子更新。

    对于独占模式而言。锁获取与释放的流程的定义则交给acquire和release两个方法,它们定义了锁获取与释放的逻辑。同一时候也是提供给子类获取和释放锁的接口。它的运行逻辑能够參考前面的“锁的获取与释放”。它提供了一个如何强大的模板?由以下的伪代码能够清晰展示出来。请注意tryAcquire和tryRelease这两个方法,它就是留给子类实现个性化的方法,通过这两个方法对共享状态的管理能够自己定义多种多样的同步器。而队列的管理及流程的控制则不是你需要考虑的问题。
    锁获取模板
    if(tryAcquire(arg)) {
        创建node
        使用CAS方式把node插入到队列尾部
        while(true){
        if(tryAcquire(arg) 而且 node的前驱节点为头节点){
    把当前节点设置为头节点
        跳出循环
    }else{
        使用CAS方式改动node前驱节点的waitStatus标识为signal
        if(改动成功)
            挂起当前线程 
    }
    }
    锁释放模板
        if(tryRelease(arg)){
    唤醒兴许节点包括的线程
    }


    我们能够觉得同步器可实现不论什么不同锁的语义。一般提供给使用者的锁是用AQS框架封装实现的更高层次的实现,提供一种更加形象的API让使用者使用起来更加方便简洁。而不是让使用者直接接触AQS框架,比如。ReentrantLock、Semphore、CountDownLatch等等。这些不同的形象的锁让你使用起来更好理解更加得心应手,并且不easy混淆。

    然而这些锁都是由AQS实现。AQS同步器面向的是线程和状态的控制,定义了线程获取状态的机制及线程排队等操作,非常好地隔离了两者的关注点,高层关注的是场景的使用,而AQS同步器则关心的是并发的控制。

    假如你要实现一个自己定义同步装置,官方推荐的做法是将集成AQS同步器的子类作为同步装置的内部类,而同步装置中相关的操作仅仅需代理成子类中相应的方法就可以。往下用一个简单的样例看看怎样实现自己的锁,因为同步器被分为两种模式。独占模式和共享模式。所以样例也相应给出。


    独占模式。独占模式採取的样例是银行服务窗体,假如某个银行网点仅仅有一个服务窗体,那么此银行服务窗体仅仅能同一时候服务一个人。其它人必须排队等待,所以这样的银行窗体同步装置是一个独占模型。

    第一个类是银行窗体同步装置类。它依照推荐的做法使用一个继承AQS同步器的子类实现,并作为子类出现。第二个类是測试类,形象一点地说。有三位良民到银行去办理业务,各自是tom、jim和jay,我们使用BankServiceWindow就能够约束他们排队,一个一个轮着办理业务而避免陷入混乱的局面。
    public class BankServiceWindow {
    private final Sync sync;
    public BankServiceWindow() {
    sync = new Sync();
    }
    private static class Sync extends AbstractQueuedSynchronizer {
    public boolean tryAcquire(int acquires) {
    if (compareAndSetState(0, 1)) {
    setExclusiveOwnerThread(Thread.currentThread());
    return true;
    }
    return false;
    }
    protected boolean tryRelease(int releases) {
    if (getState() == 0)
    throw new IllegalMonitorStateException();
    setExclusiveOwnerThread(null);
    setState(0);
    return true;
    }
    }
    public void handle() {
    sync.acquire(1);
    }
    public void unhandle() {
    sync.release(1);
    }
    }


    public class BankServiceWindowTest {
       public static void main(String[] args){
       final BankServiceWindow bankServiceWindow=new BankServiceWindow();
       Thread tom=new Thread(){
       public void run(){
       bankServiceWindow.handle();
       System.out.println("tom開始办理业务");
       try {
       this.sleep(5000);
       } catch (InterruptedException e) {
       e.printStackTrace();
       }
       System.out.println("tom结束办理业务");
       bankServiceWindow.unhandle();
       }
       };
       Thread jim=new Thread(){
       public void run(){
       bankServiceWindow.handle();
       System.out.println("jim開始办理业务");
       try {
       this.sleep(5000);
       } catch (InterruptedException e) {
       e.printStackTrace();
       }
       System.out.println("jim结束办理业务");
       bankServiceWindow.unhandle();
       }
       };
       Thread jay=new Thread(){
       public void run(){
       bankServiceWindow.handle();
       System.out.println("jay開始办理业务");
       try {
       this.sleep(5000);
       } catch (InterruptedException e) {
       e.printStackTrace();
       }
       System.out.println("jay结束办理业务");
       bankServiceWindow.unhandle();
       }
       };
       tom.start();
       jim.start();
       jay.start();
        }
    }
    输出结果例如以下:
    tom開始办理业务
    tom结束办理业务
    jim開始办理业务
    jim结束办理业务
    jay開始办理业务
    jay结束办理业务
    明显tom、jim、jay仨是排队完毕的。可是无法保证三者的顺序,可能是tom、jim、jay,也可能是tom、jay、jim。由于在入列曾经的运行先后是无法确定的,它的语义是保证一个接一个办理。

    假设没有同步器限制的情况。输出结果将不可预測。可能为输出例如以下:
    jim開始办理业务
    jay開始办理业务
    tom開始办理业务
    jay结束办理业务
    jim结束办理业务
    tom结束办理业务


    共享模式,共享模式採取的样例相同是银行服务窗体,随着此网点的发展。办理业务的人越来越多,一个服务窗体已经无法满足需求,于是又分配了一位员工开了另外一个服务窗体,这时就能够同一时候服务两个人了,但两个窗体都有人占用时相同也必须排队等待,这样的服务窗体同步器装置就是一个共享型。第一个类是共享模式的同步装置类,跟独占模式不同的是它的状态的初始值能够自由定义,获取与释放就是对状态递减和累加操作。第二个类是測试类,tom、jim和jay再次来到银行,一个有两个窗体甚是高兴,他们能够两个人同一时候办理了,时间缩减了不少。
    public class BankServiceWindows {
    private final Sync sync;
    public BankServiceWindows(int count) {
    sync = new Sync(count);
    }
    private static class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) {
    setState(count);
    }
    public int tryAcquireShared(int interval) {
    for (;;) {
    int current = getState();
    int newCount = current - 1;
    if (newCount < 0 || compareAndSetState(current, newCount)) {
    return newCount;
    }
    }
    }
    public boolean tryReleaseShared(int interval) {
    for (;;) {
    int current = getState();
    int newCount = current + 1;
    if (compareAndSetState(current, newCount)) {
    return true;
    }
    }
    }
    }


    public void handle() {
    sync.acquireShared(1);
    }


    public void unhandle() {
    sync.releaseShared(1);
    }


    }


    public class BankServiceWindowsTest {
    public static void main(String[] args){
    final BankServiceWindows bankServiceWindows=new BankServiceWindows(2);
    Thread tom=new Thread(){
    public void run(){
    bankServiceWindows.handle();
    System.out.println("tom開始办理业务");
    try {
    this.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("tom结束办理业务");
    bankServiceWindows.unhandle();
    }
    };
    Thread jim=new Thread(){
    public void run(){
    bankServiceWindows.handle();
    System.out.println("jim開始办理业务");
    try {
    this.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("jim结束办理业务");
    bankServiceWindows.unhandle();
    }
    };
    Thread jay=new Thread(){
    public void run(){
    bankServiceWindows.handle();
    System.out.println("jay開始办理业务");
    try {
    this.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("jay结束办理业务");
    bankServiceWindows.unhandle();
    }
    };
    tom.start();
    jim.start();
    jay.start();
    }
    }
    可能的输出结果为:
    tom開始办理业务
    jay開始办理业务
    jay结束办理业务
    tom结束办理业务
    jim開始办理业务
    jim结束办理业务
    tom和jay差点儿同一时候開始办理业务,而jay结束后有空暇的服务窗体jim才过来。
    这节主要讲的是怎样使用AQS构建自己的同步器。而且剖析了锁获取与释放的模板的逻辑,让你更好理解AQS的实现,最后分别给出了独占模式和共享模式的同步器实现的样例。相信你们搞清楚这两种方式的实现后。要构建更加复杂的同步器就知道力往哪里使了。



    喜欢研究java的同学能够交个朋友。以下是本人的微信号:




  • 相关阅读:
    PHP 支持中文目录和文件的的遍历:文件编码转换
    SQL server怎么查找某个时间段(多个时间段)的第一个值 或 最后一个值(这里举例查找每小时的第一个值)(Convert详细方法)
    SQL server怎么查找某个时间段(多个时间段)的第一个值 或 最后一个值(这里举例查找每小时的第一个值)(Convert详细方法)...
    SQL server怎么查找某个时间段(多个时间段)的第一个值 或 最后一个值(这里举例查找每小时的第一个值)(Convert详细方法)...
    SQL server怎么查找某个时间段(多个时间段)的第一个值 或 最后一个值(这里举例查找每小时的第一个值)(Convert详细方法)...
    SQL server 导入数据 (excel导入到SQL server数据库)
    SQL server 导入数据 (excel导入到SQL server数据库)
    SQL server 导入数据 (excel导入到SQL server数据库)
    SQL server 导入数据 (excel导入到SQL server数据库)
    excel表 更改后缀名 xlsx转成csv csv转换xlsx
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/6789902.html
Copyright © 2011-2022 走看看