美文网首页
ReadWriteLock和CountDownLatch

ReadWriteLock和CountDownLatch

作者: topshi | 来源:发表于2019-05-07 16:39 被阅读0次

    ReadWriteLock读写锁

    读写分离锁可以有效帮助减少锁竞争以提高系统性能。比如线程A1、A2、A3进行写操作,B1、B2、B3进行读操作,如果使用重入锁或者内部锁,那么所有读之间、读写之间、写写之间都是串行执行的。无论哪一个操作先获得了锁,其他操作都必须等待锁。而读写锁则允许多个线程同时读,使得B1、B2、B3之间并行执行。考虑到数据的完整性,写写操作和读写操作间需要相互等待和持有锁。

    非阻塞 阻塞
    阻塞 阻塞
    • 读-读不互斥:读读之间不阻塞
    • 读-写互斥:读阻塞写,写也阻塞读
    • 写-写互斥:写写阻塞

    如果某个系统的读操作次数远远大于写操作,则读写锁可以发挥最大功效

    package readWriteLock;
    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    /**
     * @Time : 2019/05/07 下午 03:30
     * @Author : xiuc_shi
     **/
    public class ReadWriteLockDemo {
        private static ReentrantLock lock = new ReentrantLock();
        private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private static Lock readLock = readWriteLock.readLock();
        private static Lock writeLock = readWriteLock.writeLock();
        public int value;
        //模拟读操作
        public int readData(Lock lock){
            try {
                lock.lock();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            return value;
        }
        //模拟写操作
        public void writeData(Lock lock, int value){
            try {
                lock.lock();
                Thread.sleep(1000);
                this.value = value;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            ReadWriteLockDemo demo = new ReadWriteLockDemo();
            ExecutorService es = Executors.newFixedThreadPool(20);
            Runnable readTask = new Runnable() {
                @Override
                public void run() {
                    demo.readData(readLock);
                }
            };
            Runnable writeTask = new Runnable() {
                @Override
                public void run() {
                    demo.writeData(writeLock,new Random().nextInt(10));
                }
            };
    //        long time = System.currentTimeMillis();
            for(int i = 0;i < 18;i++){
                es.submit(readTask);
            }
            for(int i = 18;i < 20;i++){
                es.submit(writeTask);
            }
            es.shutdown();
    //        System.out.println("运行用时:" + (System.currentTimeMillis() - time));
        }
    }
    

    以上例子如果使用重入锁,执行时间大概需要20秒,因为每个操作都要等待锁。而换成读写锁,读操作不阻塞,执行时间大概2秒。
    ps:本来想通过程序输出执行时间的,后来发现我还是太年轻,脑子里时刻要想到的是多线程,主程序将所有子线程提交之后,马上就继续执行了,算出来的时间根本不是所有子线程执行总时长,应该等待所有子线程执行完,主线程再执行,才能输出时长。对于计算时长这个问题,我们使用CountDownLatch来完成。

    倒计时器:CountDownLatch

    CountDownLatch通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。
    CountDownLatch的构造函数接受一个整数作为参数,表示计数器的计数个数。

      public CountDownLatch(int count) 
    

    那么我们ReadWriteLockDemo执行了20个线程任务,那么我们创建一个倒计时为20CountDownLatch对象,每完成一个任务减1,倒计时完成再继续执行主线程。

    package readWriteLock;
    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    /**
     * @Time : 2019/05/07 下午 03:30
     * @Author : xiuc_shi
     **/
    public class ReadWriteLockDemo {
        private static ReentrantLock lock = new ReentrantLock();
        private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private static Lock readLock = readWriteLock.readLock();
        private static Lock writeLock = readWriteLock.writeLock();
        public int value;
        //模拟读操作
        public int readData(Lock lock){
            try {
                lock.lock();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            return value;
        }
        //模拟写操作
        public void writeData(Lock lock, int value){
            try {
                lock.lock();
                Thread.sleep(1000);
                this.value = value;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            ReadWriteLockDemo demo = new ReadWriteLockDemo();
            ExecutorService es = Executors.newFixedThreadPool(20);
            //创建一个倒计时为20的倒计时器
            CountDownLatch cdl = new CountDownLatch(20);
            Runnable readTask = new Runnable() {
                @Override
                public void run() {
                    demo.readData(readLock);
                    //任务完成,倒计时减1
                    cdl.countDown();
                }
            };
            Runnable writeTask = new Runnable() {
                @Override
                public void run() {
                    demo.writeData(writeLock,new Random().nextInt(10));
                    //任务完成,倒计时减1
                    cdl.countDown();
                }
            };
            long time = System.currentTimeMillis();
            for(int i = 0;i < 18;i++){
                es.submit(readTask);
            }
            for(int i = 18;i < 20;i++){
                es.submit(writeTask);
            }
            es.shutdown();
            //等待倒计时完成
            cdl.await();
            System.out.println("运行用时:" + (System.currentTimeMillis() - time)/1000 + "s");
        }
    }
    >>> 结果
    运行用时:3s
    

    cdl.countDown()方法通知CountDownLatch,一个线程完成了任务,倒计时器可以减1cdl.await()方法要求主线程等待所有20个线程全部执行完任务才能继续执行。

    相关文章

      网友评论

          本文标题:ReadWriteLock和CountDownLatch

          本文链接:https://www.haomeiwen.com/subject/jkhyoqtx.html