zoukankan      html  css  js  c++  java
  • java并发编程(7)构建自定义同步工具及条件队列

    构建自定义同步工具

    一、通过轮询与休眠的方式实现简单的有界缓存

    public void put(V v) throws InterruptedException {
            while (true) {                          //轮询
                synchronized (this) {               //加锁等待
                    if (!isFull()) {                //如果缓存没满 则执行插入
                        doPut(v);
                        return;
                    }
                }
                Thread.sleep(SLEEP_GRANULARITY);    //如果缓存满了 线程等待一段时间后继续轮询
            }
        }
    
        public V take() throws InterruptedException {//同理 相反
            while (true) {
                synchronized (this) {
                    if (!isEmpty())
                        return doTake();
                }
                Thread.sleep(SLEEP_GRANULARITY);
            }
        }

    二、通过条件队列:wait notify方法

       注意:实际逻辑与上面没区别;且无法通过轮询和休眠方式实现的,也无法通过条件队列实现

       

        // BLOCKS-UNTIL: not-full
        public synchronized void put(V v) throws InterruptedException {
            while (isFull()) {
                wait();             //等待:释放锁且等待;等待通知后获取锁 并继续判断条件谓词
            }
            doPut(v);
            notifyAll();            //通知:通知释放所有等待
        }
     
        // 问题,不好分析,在put 和 take操作后,通知了所有等待条件队列
        // 而且让所有 等待都要去判断 条件谓词;
        // 如我put完成,只需要通知take正在等待的,而无需通知put正在等待的(反之亦然),因为只有take后,缓存才有可能不是满的,才要去判断条件谓词
        public synchronized V take() throws InterruptedException {
            while (isEmpty()) {
                wait();
            }
            V v = doTake();
            notifyAll();
            return v;
        }

     三、内置条件队列的使用

        条件谓词:对象在哪个条件下等待 

      条件队列:每次唤醒时,必须重新检查条件谓词

     四、显式锁Condition:自定义条件队列

      内置条件队列:每个内置锁只能有一个相关联的条件队列

        所以:多个线程在一个条件队列上等待不同的条件谓词不好解决

        方法:wait、notify、notifyAll

      而Condition:每个Lock,可以有任意的Condition

        方法:await、signal、signalAll

    优化内置的条件队列:

    public class ConditionBoundedBuffer <T> {
        protected final Lock lock = new ReentrantLock();
        // 条件谓词 未满
        private final Condition notFull = lock.newCondition();
        // 条件谓词 非空
        private final Condition notEmpty = lock.newCondition();
        private static final int BUFFER_SIZE = 100;
        private final T[] items = (T[]) new Object[BUFFER_SIZE];
        private int tail, head, count;
    
        // BLOCKS-UNTIL: notFull
        public void put(T x) throws InterruptedException {
            lock.lock();
            try {
                while (count == items.length)
                    notFull.await();    //当缓存满了,等待有线程取出缓存;等待未满谓词通知(只有take才有可能让缓存不满)
                items[tail] = x;
                if (++tail == items.length)
                    tail = 0;
                ++count;
                notEmpty.signal();      //当插入成功,通知释放非空谓词
            } finally {
                lock.unlock();
            }
        }
    
        // BLOCKS-UNTIL: notEmpty
        public T take() throws InterruptedException {
            lock.lock();
            try {
                while (count == 0)
                    notEmpty.await();   //当缓存为空,等待插入;等待非空谓词通知
                T x = items[head];
                items[head] = null;
                if (++head == items.length)
                    head = 0;
                --count;
                notFull.signal();               //当取出一个,通知释放未满谓词
                return x;
            } finally {
                lock.unlock();
            }
        }
    }

    五、AQS:AbstractQueuedSynchronizer

      先来看一个例子:

    //通过 lock+条件队列实现信号量Semaphore 许可
    public class SemaphoreOnLock {
        private final Lock lock = new ReentrantLock();
        // CONDITION PREDICATE: permitsAvailable (permits > 0)
        private final Condition permitsAvailable = lock.newCondition();
        private int permits;
    
        SemaphoreOnLock(int initialPermits) {
            lock.lock();
            try {
                permits = initialPermits;
            } finally {
                lock.unlock();
            }
        }
    
        // BLOCKS-UNTIL: permitsAvailable
        public void acquire() throws InterruptedException {
            lock.lock();
            try {
                while (permits <= 0)            //当许可<0时 等待 许可释放
                    permitsAvailable.await();
                --permits;
            } finally {
                lock.unlock();
            }
        }
    
        public void release() {
            lock.lock();
            try {
                ++permits;                      //释放许可
                permitsAvailable.signal();
            } finally {
                lock.unlock();
            }
        }
    }

      AQS负责管理同步器类中的状态,它管理了一个整数状态信息;

      如:通过AQS实现简单的闭锁

    public class OneShotLatch {
        private final Sync sync = new Sync();
    
        public void signal() {
            sync.releaseShared(0);
        }
    
        public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(0); 
        }
    
        private class Sync extends AbstractQueuedSynchronizer { //继承AQS
            protected int tryAcquireShared(int ignored) {
                // 判断状态
                return (getState() == 1) ? 1 : -1;
            }
    
            protected boolean tryReleaseShared(int ignored) {
                //释放状态
                setState(1); // 打开闭锁
                return true; // 其他线程可以获取该闭锁
    
            }
        }
    }

    小结

      1.最好使用现有的类库来构建状态类:如缓存

      2.如果现有类不能满足功能:如从一个空队列中删除或获取元素会抛出异常,当然也有阻塞等待

        那么使用Lock、内置的条件队列、Condition显式的条件队列、AQS等来构建自己的同步器也是可行的

        内置条件队列与内置锁关联、显式的条件队列与Lock关联

  • 相关阅读:
    android 第三方开源库 学习汇总之Butter Knife
    android Gradle下载慢,使用阿里镜像
    android 第三方开源库 学习汇总
    <Android Studio> 4.Adapter的那些事 <一>
    <Android Studio> 3.打包APK
    <Android Studio> 2.APP开机启动
    <Android Studio> 1.如何APP配置权限
    Android源码分析(十七)----init.rc文件添加脚本代码
    Android源码分析(十六)----adb shell 命令进行OTA升级
    Android源码分析(十五)----GPS冷启动实现原理分析
  • 原文地址:https://www.cnblogs.com/zhangxinly/p/6946337.html
Copyright © 2011-2022 走看看