博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AQS同步组件--ReentrantLock与锁
阅读量:6993 次
发布时间:2019-06-27

本文共 6576 字,大约阅读时间需要 21 分钟。

ReentrantLock与锁

Synchronized和ReentrantLock异同
  • 可重入性:两者都具有可重入性
  • 锁的实现:Synchronized是依赖jvm实现的,ReentrantLock是jdk实现的。(我们可以理解为一个是操作系统层面的实现另一个是用户自己自己实现的)Synchronized的实现是jvm层面的很难看到其中的实现。而ReentrantLock是通过jvm实现的我们可以通过阅读jvm源码来查看实现。
  • 性能区别:在Synchronized优化之前Synchronized的性能相比ReentrantLock差很多,在Synchronized引入了偏向锁,轻量级锁也就是自旋锁之后了,两者的性能相差不大了。在两者都可用的情况下官方更推荐使用Synchronized,因为其写法更简单,Synchronized的优化就是借鉴了ReentrantLock中的cas技术。
  • 功能区别:便利性,很明显synchronized的使用更加便利,ReentrantLock在细粒度和灵活性中会优于Synchronized。
ReentrantLock独有功能
  • ReentrantLock可指定是公平锁还是非公平锁,Synchronized只能是非公平锁。(公平锁就是先等待的线程先获得锁)
  • ReentrantLock提供一个Condition类,可以分组唤醒需要唤醒的形成。synchronized是要嘛随机唤醒一个线程要嘛唤醒所有的线程。
  • ReentrantLock提供了一种能够中断等待锁的线程的机制lock.locInterruptibly(),ReentrantLock实现是一种自旋锁通过循环调用,通过cas机制来实现加锁。性能较好是因为避免了线程进入内核的阻塞状态
@Slf4jpublic class LockExample2 {    // 请求总数    public static int clientTotal = 5000;    // 同时并发执行的线程数    public static int threadTotal = 200;    public static int count = 0;    private final static Lock lock = new ReentrantLock();    public static void main(String[] args) throws Exception {        ExecutorService executorService = Executors.newCachedThreadPool();        final Semaphore semaphore = new Semaphore(threadTotal);        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);        for (int i = 0; i < clientTotal ; i++) {            executorService.execute(() -> {                try {                    semaphore.acquire();                    add();                    semaphore.release();                } catch (Exception e) {                    log.error("exception", e);                }                countDownLatch.countDown();            });        }        countDownLatch.await();        executorService.shutdown();        log.info("count:{}", count);    }    private static void add() {        lock.lock();        try {            count++;        } finally {            lock.unlock();        }    }}复制代码

我们首先使用 private final static Lock lock = new ReentrantLock()声明一个所得实例,然后使用

lock.lock();        try {            count++;        } finally {            lock.unlock();        }复制代码

进行加锁和解锁操作。

我们在通过一个例子来看看这个ReentrantReadWriteLock怎么用。

@Slf4jpublic class LockExample3 {    private final Map
map = new TreeMap<>(); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); public Data get(String key) { readLock.lock(); try { return map.get(key); } finally { readLock.unlock(); } } public Set
getAllKeys() { readLock.lock(); try { return map.keySet(); } finally { readLock.unlock(); } } public Data put(String key, Data value) { writeLock.lock(); try { return map.put(key, value); } finally { readLock.unlock(); } } class Data { }}复制代码

通过 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock()声明一个ReentrantReadWriteLock,然后再分别获取 private final Lock readLock = lock.readLock() private final Lock writeLock = lock.writeLock()读锁和写锁。 我们在这个map读的时候加上读锁在写的时候加上写锁,但是这里有问题就是这个锁是悲观锁,也就是说在执行写锁的时候一定不能有读锁,当读操作特 特别多的时候很有可能会让写锁一直无法执行。

我们看一下官方的例子学习一下,StampedLock

import java.util.concurrent.locks.StampedLock;public class LockExample4 {    class Point {        private double x, y;        private final StampedLock sl = new StampedLock();        void move(double deltaX, double deltaY) { // an exclusively locked method            long stamp = sl.writeLock();            try {                x += deltaX;                y += deltaY;            } finally {                sl.unlockWrite(stamp);            }        }        //下面看看乐观读锁案例        double distanceFromOrigin() { // A read-only method            long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁            double currentX = x, currentY = y;  //将两个字段读入本地局部变量            if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?                stamp = sl.readLock();  //如果没有,我们再次获得一个读悲观锁                try {                    currentX = x; // 将两个字段读入本地局部变量                    currentY = y; // 将两个字段读入本地局部变量                } finally {                    sl.unlockRead(stamp);                }            }            return Math.sqrt(currentX * currentX + currentY * currentY);        }        //下面是悲观读锁案例        void moveIfAtOrigin(double newX, double newY) { // upgrade            // Could instead start with optimistic, not read mode            long stamp = sl.readLock();            try {                while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合                    long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁                    if (ws != 0L) { //这是确认转为写锁是否成功                        stamp = ws; //如果成功 替换票据                        x = newX; //进行状态改变                        y = newY;  //进行状态改变                        break;                    } else { //如果不能成功转换为写锁                        sl.unlockRead(stamp);  //我们显式释放读锁                        stamp = sl.writeLock();  //显式直接进行写锁 然后再通过循环再试                    }                }            } finally {                sl.unlock(stamp); //释放读锁或写锁            }        }    }}复制代码

我们再将前面的里改成StampedLock

@Slf4jpublic class LockExample5 {    // 请求总数    public static int clientTotal = 5000;    // 同时并发执行的线程数    public static int threadTotal = 200;    public static int count = 0;    private final static StampedLock lock = new StampedLock();    public static void main(String[] args) throws Exception {        ExecutorService executorService = Executors.newCachedThreadPool();        final Semaphore semaphore = new Semaphore(threadTotal);        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);        for (int i = 0; i < clientTotal ; i++) {            executorService.execute(() -> {                try {                    semaphore.acquire();                    add();                    semaphore.release();                } catch (Exception e) {                    log.error("exception", e);                }                countDownLatch.countDown();            });        }        countDownLatch.await();        executorService.shutdown();        log.info("count:{}", count);    }    private static void add() {        long stamp = lock.writeLock();        try {            count++;        } finally {            lock.unlock(stamp);        }    }}复制代码

这里和之前的不一样的地方就是

long stamp = lock.writeLock();        try {            count++;        } finally {            lock.unlock(stamp);        }复制代码

在加锁后会返回一个值,解锁的时候需要传入这个值。

转载地址:http://xzcvl.baihongyu.com/

你可能感兴趣的文章
影像分辨率、地面分辨率、比例尺及DPI之间的关系
查看>>
ab命令
查看>>
VIM使用系列:缩进调整、自动补全、折叠、跳转
查看>>
批处理练手(2015-6-22)
查看>>
ONS(RocketMQ)为什么能够比Kafka支持更多的分区数量?
查看>>
MySql高可用集群搭建
查看>>
启动界面StartActivity
查看>>
jquery 滚动条到底部
查看>>
在java中获取服务器多网卡的ip地址
查看>>
数据存储
查看>>
javascript 函数的4种调用模式
查看>>
使用开源中国第三方Maven库
查看>>
android so 文件存私密数据,且防止 so文件未知应用盗用
查看>>
Playing with sockets (port scanning)
查看>>
Golang优秀开源项目汇总(持续更新。。。)
查看>>
jps命令的使用
查看>>
PostgreSQL 入门小白基础
查看>>
CSS缩进,CSS字间距
查看>>
Android studio NDK-JNI HelloJNI
查看>>
谈一谈mysql给我带来的中文乱码问题
查看>>