美文网首页
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