高并发编程之JUC 三大辅助类和读写锁

news2024/11/24 19:43:13

7 JUC 三大辅助类

JUC 中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过

多时 Lock 锁的频繁操作。这三种辅助类为:

  • • CountDownLatch: 减少计数
  • • CyclicBarrier: 循环栅栏
  • • Semaphore: 信号灯

下面我们分别进行详细的介绍和学习

7.1 减少计数 CountDownLatch

CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行

减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法

之后的语句。

• CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这

些线程会阻塞

• 其它线程调用 countDown 方法会将计数器减 1(调用 countDown 方法的线程

不会阻塞)

• 当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行

场景: 6 个同学陆续离开教室后值班同学才可以关门。

CountDownLatchDemopackage com.at

package com.xingchen.juc;

import java.util.concurrent.CountDownLatch;

//演示 CountDownLatch

/**
 * @author xing'chen
 */
public class CountDownLatchDemo {
    //6个同学陆续离开教室之后,班长锁门
    
    public static void main(String[] args) throws InterruptedException {

        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //6个同学陆续离开教室之后
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");

                //计数  -1
                countDownLatch.countDown();

            },String.valueOf(i)).start();
        }

        //等待
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
    }
}

7.2 循环栅栏 CyclicBarrier

CyclicBarrier 看英文单词可以看出大概就是循环阻塞的意思,在使用中

CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一

次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后

的语句。可以将 CyclicBarrier 理解为加 1 操作

场景: 集齐 7 颗龙珠就可以召唤神龙

CyclicBarrierDemo

package com.xingchen.juc;

import java.util.concurrent.CyclicBarrier;

//集齐7颗龙珠就可以召唤神龙
/**
 * @author xing'chen
 */
public class CyclicBarrierDemo {

    //创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        //创建CyclicBarrier
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUMBER,()->{
                    System.out.println("*****集齐7颗龙珠就可以召唤神龙");
                });

        //集齐七颗龙珠过程
        for (int i = 1; i <=7; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+" 星龙被收集到了");
                    //等待
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

7.3 信号灯 Semaphore

Semaphore 的构造方法中传入的第一个参数是最大信号量(可以看成最大线

程池),每个信号量初始化为一个最多只能分发一个许可证。使用 acquire 方

法获得许可证,release 方法释放许可

场景: 抢车位, 6 部汽车 3 个停车位

SemaphoreDemo

package com.xingchen.juc;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author xing'chen
 */ //6辆汽车,停3个车位
public class SemaphoreDemo {
    public static void main(String[] args) {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);

        //模拟6辆汽车
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                try {
                    //抢占
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName()+" 抢到了车位");

                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName()+" ------离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

8 读写锁

8.1 读写锁介绍

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那

么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以

应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,

就不应该允许其他线程对该资源进行读和写的操作了。

针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock,

它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称

为排他锁

1. 线程进入读锁的前提条件:

• 没有其他线程的写锁

• 没有写请求, 或者有写请求,但调用线程和持有锁的线程是同一个(可重入锁)。

2. 线程进入写锁的前提条件:

• 没有其他线程的读锁

• 没有其他线程的写锁

而读写锁有以下三个重要的特性:

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公

平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为

读锁。

8.2 ReentrantReadWriteLock

ReentrantReadWriteLock 类的整体结构

public class ReentrantReadWriteLock implements ReadWriteLock,
        java.io.Serializable {
    /** 读锁 */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** 写锁 */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    final Sync sync;

    /** 使用默认(非公平)的排序属性创建一个新的
     ReentrantReadWriteLock */
    public ReentrantReadWriteLock() {
        this(false);
    }
    /** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
    /** 返回用于写入操作的锁 */
    public ReentrantReadWriteLock.WriteLock writeLock() { return
            writerLock; }

    /** 返回用于读取操作的锁 */
    public ReentrantReadWriteLock.ReadLock readLock() { return
            readerLock; }
    abstract static class Sync extends AbstractQueuedSynchronizer {}
    static final class NonfairSync extends Sync {}
    static final class FairSync extends Sync {}
    public static class ReadLock implements Lock, java.io.Serializable {}
    public static class WriteLock implements Lock, java.io.Serializable {}
}

可以看到,ReentrantReadWriteLock 实现了 ReadWriteLock 接口,

ReadWriteLock 接口定义了获取读锁和写锁的规范,具体需要实现类去实现;

同时其还实现了 Serializable 接口,表示可以进行序列化,在源代码中可以看

到 ReentrantReadWriteLock 实现了自己的序列化逻辑。

8.3 入门案例

场景: 使用 ReentrantReadWriteLock 对一个 hashmap 进行读和写操作

8.3.1 实现案例

package com.xingchen.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

//资源类
class MyCache {
    //创建map集合

    private volatile Map<String,Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}

/**
 * @author xing'chen
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }

        TimeUnit.MICROSECONDS.sleep(300);

        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

8.4 小结(重要)

• 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发

现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。

• 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写

锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。

原因: 当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把

获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写

锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释

放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript基础之循环

1、循环 1.1、for循环 语法结构&#xff1a; for(初始化变量; 条件表达式; 操作表达式 ){//循环体 } 名称作用初始化变量通常被用于初始化一个计数器&#xff0c;该表达式可以使用 var 关键字声明新的变量&#xff0c;这个变量帮我们来记录次数。条件表达式用于确定每一次循…

Java on Azure Tooling 11月更新|远程调试支持与 App Settings 增强

作者&#xff1a;Jialuo Gan Program Manager, Developer Division at Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎回到11月的 Java on Azure Tooling 的更新。在这次更新中&#xff0c;我们将引入对 Azure Spring Apps 和 Azure Function Apps 的远程调试支持…

MySql补充知识点

这里写自定义目录标题1、sql语句查询表结构信息(1)查询某库某表的字段、数据类型、字段注释(2)查询某库的所有表名、表注释(3)查询库下所有表名、表注释、所有字段名、数据类型、字段注释(4)查询某个表在哪个库2、MySQL操作符(1)Union(2)having、on、where的区别having、whereo…

虚拟化与云计算技术硬核内幕

这种将物理硬件分配给多个使用者的技术&#xff0c;叫做“时分复用”。计算机操作系统的任务调度模块&#xff0c;实质上提供的就是将CPU以“时分复用”的方式给不同任务使用的机制。 那么&#xff0c;如果在虚拟化系统中&#xff0c;也利用时分复用机制&#xff0c;将一个物理…

Pspice——Source用法的汇总

信号源&#xff08;Source&#xff09;的用法 库所在位置&#xff1a;…CAPTURE\LIBRARY\PSPICE\SOURCE.OLB 模拟信号源列表 信号源 类型 用法 适用场景 VAC/IAC 交流 电压源/电流源 VDC/IDC 直流 电压源/电流源 直流特性分析、瞬态分析 VEXP/IEXP 指数 电压…

MVCC 多版本并发控制

Multi-Version Concurrency Control 多版本并发控制&#xff0c;MVCC 是一种并发控制的方法&#xff0c; 一般在数据库管理系统中&#xff0c;实现对数据库的并发访问&#xff1b;在编程语言中实现事务内存。 实现原理 放在中间件的用户进程通过tcp/ip连接到服务器上 服务器给…

ssm+Vue计算机毕业设计校园学生管理系统(程序+LW文档)

ssmVue计算机毕业设计校园学生管理系统&#xff08;程序LW文档&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技…

FENeRF: Face Editing in Neural Radiance Fields翻译

论文地址 代码地址 摘要: 以前的肖像生成方法大致分为两类&#xff1a;2D GAN和3D感知GAN。2D GAN可以生成高保真的肖像&#xff0c;但具有低的视觉一致性。3D感知GAN方法可以保持视觉一致性&#xff0c;但其生成的图像不具有局部可编辑性。为了克服这些限制&#xff0c;我们…

Java Timer(定时器)

概述&#xff1a; Timer是一种定时器工具&#xff0c;用来在一个后台线程计划执行指定任务。它可以安排任务“执行一次”或者定期“执行多次”。 然而在实际的开发过程当中&#xff0c;经常需要一些周期性的操作&#xff0c;比如每5分钟执行某一操作等。对于这…

【AI理论学习】Graph Embedding理论介绍及5种算法演示(DeepWalk)

Graph Embedding理论介绍及5种算法演示1.图数据结构2.图表示学习3.Graph Embedding3.1 DeepWalk算法DeepWalk算法理论DeepWalk 核心代码参考资料1.图数据结构 在现实世界中&#xff0c;网络只是互连节点的集合。为了表示这种类型的网络&#xff0c;我们需要一个与之相似的数据…

SDV进入新阶段,如何让智能汽车应用开发变得更容易?

软件定义汽车加速到来&#xff0c;基于域控制器架构的车型大规模量产&#xff0c;从域控架构到中央集成式架构&#xff0c;跨域融合已经成为明显趋势。 高工智能汽车研究院发布《2023-2025年智能网联产业趋势报告》显示&#xff0c;今年1-10月&#xff0c;智驾域控制器前装搭载…

【Kafka】Kafka 简介 || CentOS7 安装 Kafka || SpringBoot整合 Kafka 方式

最近的精神状态真的不是很好&#xff0c;刚刚脱离&#x1f40f;羊的苦海&#xff0c;收获了很多吧&#xff0c;任何经历都是我们成长的关键。本文是我 Kafka 入门部分的一个笔记&#xff0c;大家如果有有疑问的地方可以评论区或者私信我&#xff0c;我看见了都会回复的。最后&a…

安卓玩机搞机技巧综合资源-----手机隐藏拍照录像 取证软件 寻找隐藏摄像头 【十六】

接上篇 安卓玩机搞机技巧综合资源------如何提取手机分区 小米机型代码分享等等 【一】 安卓玩机搞机技巧综合资源------开机英文提示解决dm-verity corruption your device is corrupt. 设备内部报错 AB分区等等【二】 安卓玩机搞机技巧综合资源------EROFS分区格式 小米红…

计算机网络学习笔记(V):传输层

目录 1 传输层概述 1.1 功能 1.2 两种协议 1.TCP 2.UDP协议 1.3 传输层的寻址与端口 2 UDP协议 2.1 特点 2.2 首部格式 2.3 UDP检验 3 TCP协议 3.1 TCP协议 1.特点 2.报文段首部格式 3.2 TCP连接管理 1.连接建立 2.TCP连接释放 3.3 TCP可靠传输 1.校验 2.序…

java+MySQL 基于ssm的视频播放网站

随着现代视频播放网站管理的快速发展,可以说视频播放网站管理已经逐渐成为现代视频播放网站管理过程中最为重要的部分之一。但是一直以来我国传统的视频播放网站管理并没有建立一套完善的行之有效的视频播放网站管理系统,传统的视频播放网站管理已经无法适应高速发展,无论是从效…

中小型水库雨水情测报平台有哪些功能?水库雨水情数据孪生安全监测系统

平升电子中小型水库雨水情测报平台/雨水情监测及视频监控解决方案/水库雨水情数据孪生安全监测系统统辅助水利管理部门实现水库雨水情信息“全要素、全量程、全覆盖”自动测报。系统具备水库水位、雨量、现场图像/视频等水文信息采集、传输、处理及预警广播等功能&#xff0c;有…

美股l2接口有什么特别的功能服务?

美股l2接口主要是面向做美股投资的level2行行情接口&#xff0c;接下来小编说说它的两大特色&#xff01; 1、买卖盘从各五档扩展到各十档 为投资者、基金、机构提供更多的交易参考数据&#xff0c;可以更准确地确定交易任务的完成程度。 美股l2接口&#xff08;十档行情快照…

【ShaderGraph】关于ShaderGraph的的介绍和入门

目录 一.ShaderGraph入门介绍 二.创建SRP工程&#xff08;URP或者HDRP&#xff09; 1.创建URP工程 2.创建HDRP工程 三.创建一个Shader Graph文件资源 ​​​​​​​ 一.ShaderGraph入门介绍 Shader Graph 使您能够直观地构建着色器。您无需编写代码&#xff0c;而是在图…

实验室管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a;模块划分&#xff1a;班级模块、老师模块、学生模块、实验室模块、试脸模块、作业模块、作业提交、作业打分 管理员功能&…

ICG-Carboxylic Acid ICG标记羧基

ICG-Carboxylic Acid ICG标记羧基 外观&#xff1a; 绿色固体粉末 CAS: 181934-09-8 分子式&#xff1a;C45H50N2O5S 相对分子质量(g/mol): 730.6 溶解性&#xff1a;DMF/DMSO 注意事项 避免长时间接触光线。 保存条件&#xff1a;-20避光。 产品应用 ICG是一种带负电…