• Java 多线程:锁


    StampedLock

    StampedLock 其实是对读写锁的一种改进,它支持在读同时进行一个写操作,也就是说,它的性能将会比读写锁更快。

    更通俗的讲就是在读锁没有释放的时候是可以获取到一个写锁,获取到写锁之后,读锁阻塞,这一点和读写锁一致,唯一的区别在于 读写锁不支持在没有释放读锁的时候获取写锁 。

    StampedLock 有三种模式:

    • 悲观读:允许多个线程获取悲观读锁。

    • 写锁:写锁和悲观读是互斥的。

    • 乐观读:无锁机制,类似于数据库中的乐观锁,它支持在不释放乐观读的时候是可以获取到一个写锁。

    参考: 有没有比读写锁更快的锁?

    示例代码:

    悲观读 + 写锁:

    package git.snippets.juc;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.locks.StampedLock;
    import java.util.logging.Logger;
    
    // 悲观读 + 写锁
    public class StampedLockPessimistic {
        private static final Logger log = Logger.getLogger(StampedLockPessimistic.class.getName());
        private static final StampedLock lock = new StampedLock();
        //缓存中存储的数据
        private static final Map mapCache = new HashMap<>();
        //模拟数据库存储的数据
        private static final Map mapDb = new HashMap<>();
    
        static {
            mapDb.put("zhangsan", "你好,我是张三");
            mapDb.put("sili", "你好,我是李四");
        }
    
        private static void getInfo(String name) {
            //获取悲观读
            long stamp = lock.readLock();
            log.info("线程名:" + Thread.currentThread().getName() + " 获取了悲观读锁" + "    用户名:" + name);
            try {
                if ("zhangsan".equals(name)) {
                    log.info("线程名:" + Thread.currentThread().getName() + " 休眠中" + "    用户名:" + name);
                    Thread.sleep(3000);
                    log.info("线程名:" + Thread.currentThread().getName() + " 休眠结束" + "    用户名:" + name);
                }
                String info = mapCache.get(name);
                if (null != info) {
                    log.info("在缓存中获取到了数据");
                    return;
                }
            } catch (InterruptedException e) {
                log.info("线程名:" + Thread.currentThread().getName() + " 释放了悲观读锁");
                e.printStackTrace();
            } finally {
                //释放悲观读
                lock.unlock(stamp);
            }
    
            //获取写锁
            stamp = lock.writeLock();
            log.info("线程名:" + Thread.currentThread().getName() + " 获取了写锁" + "    用户名:" + name);
            try {
                //判断一下缓存中是否被插入了数据
                String info = mapCache.get(name);
                if (null != info) {
                    log.info("获取到了写锁,再次确认在缓存中获取到了数据");
                    return;
                }
                //这里是往数据库获取数据
                String infoByDb = mapDb.get(name);
                //将数据插入缓存
                mapCache.put(name, infoByDb);
                log.info("缓存中没有数据,在数据库获取到了数据");
            } finally {
                //释放写锁
                log.info("线程名:" + Thread.currentThread().getName() + " 释放了写锁" + "     用户名:" + name);
                lock.unlock(stamp);
            }
        }
    
        public static void main(String[] args) {
    //线程1
            Thread t1 = new Thread(() -> {
                getInfo("zhangsan");
            });
    
            //线程2
            Thread t2 = new Thread(() -> {
                getInfo("lisi");
            });
    
            //线程启动
            t1.start();
            t2.start();
    
            //线程同步
            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    乐观读:

    package git.snippets.juc;
    
    import java.util.concurrent.locks.StampedLock;
    import java.util.logging.Logger;
    
    // 乐观写
    public class StampedLockOptimistic {
        private static final Logger log = Logger.getLogger(StampedLockOptimistic.class.getName());
        private static final StampedLock lock = new StampedLock();
        private static int num1 = 1;
        private static int num2 = 1;
    
        /**
         * 修改成员变量的值,+1
         *
         * @return
         */
        private static int sum() {
            log.info("求和方法被执行了");
            //获取乐观读
            long stamp = lock.tryOptimisticRead();
            int cnum1 = num1;
            int cnum2 = num2;
            log.info("获取到的成员变量值,cnum1:" + cnum1 + "   cnum2:" + cnum2);
            try {
                //休眠3秒,目的是为了让其他线程修改掉成员变量的值。
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //判断在运行期间是否存在写操作   true:不存在   false:存在
            if (!lock.validate(stamp)) {
                log.info("存在写操作!");
                //存在写锁
                //升级悲观读锁
                stamp = lock.readLock();
                try {
                    log.info("升级悲观读锁");
                    cnum1 = num1;
                    cnum2 = num2;
                    log.info("重新获取了成员变量的值=========== cnum1=" + cnum1 + "    cnum2=" + cnum2);
                } finally {
                    //释放悲观读锁
                    lock.unlock(stamp);
                }
            }
            return cnum1 + cnum2;
        }
    
        //使用写锁修改成员变量的值
        private static void updateNum() {
            long stamp = lock.writeLock();
            try {
                num1 = 2;
                num2 = 2;
            } finally {
                lock.unlock(stamp);
            }
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -
  • 相关阅读:
    idea类和方法配置注释模板
    SpringBoot SpringBoot 基础篇 4 基于 SpringBoot 的SSMP 整合案例 4.15 删除功能
    来一场关于数字人的灵魂辩论|BOOK DAO内容共建招募
    猿创征文 |【Ant Design Pro】使用ant design pro做为你的开发模板(一)拉取项目
    11位顶级专家共同预测2023年量子领域发展趋势
    朋友问我,你都30岁了学编程来得及吗
    SpringCloud搭建微服务之Bus消息总线
    图解哈希表及其原理
    c语言 文件操作
    Java高级:条件队列与同步器Synchronizer的原理+AQS的应用
  • 原文地址:https://blog.csdn.net/m0_73257876/article/details/126813339