juc并发编程(下)

一些辅助类

减少计数CountDownLatch

设置一个计数器,通过countDown方法进行减1操作,使用await方法等待计数器不大于0,继续执行await方法之后的语句。

  • 当一个或多个线程调用await方法时,这些线程会阻塞

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

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

public class CountDownLatchDemo {// 6位同学陆续离开教室之后班长锁门public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(6);for (int i = 0; i < 6; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName()+" left.");latch.countDown();},String.valueOf(i+1)).start();}// 等待操作,直到计数器变为0,才会执行await之后的语句latch.await();System.out.println("Close the door.");}
}

循环栅栏CyclicBarrier

CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到达到某个公共屏障点,在涉及一组固定大小的线程的程序中,这些线程必须不时地相互等待。

构造方法的第一个参数是目标障碍数,每次执行一次CyclicBarrier一次障碍数就会+1,如果达到了目标障碍数,才会执行cyclicBarrier.await()之后的语句

public class CyclicBarrierDemo {public static void main(String[] args) {// 设置固定值以及固定值达到之后的操作CyclicBarrier barrier = new CyclicBarrier(7,()->{System.out.println("Finished!!");});for (int i = 0; i < 5; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName()+" save.");try {// 每次执行到await都会有一个计数// 如果数量达到固定值,就会执行预先指定的操作// 如果数量没有达到,就一直会处于等待状态barrier.await();} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}
}

信号灯Semaphore

主要就是限制同时执行操作的进程数量

 

public class SemaphoreDemo {public static void main(String[] args) {// 创建Semaphore,设置许可数量,第二个参数表示是否为公平的Semaphore semaphore = new Semaphore(3);// 模拟6两汽车for (int i = 0; i < 6; i++) {new Thread(()->{try {// 抢占车位semaphore.acquire();System.out.println(Thread.currentThread().getName()+" Got It.");// 设置随机停车时间TimeUnit.SECONDS.sleep(new Random().nextInt(5));System.out.println(Thread.currentThread().getName()+" Leave.");} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放许可semaphore.release();}}, String.valueOf(i)).start();}}
}

ReentrantReadWriteLock读写锁

悲观锁

乐观锁

读锁发生死锁

写锁发生死锁

 

class MyCache {private volatile HashMap<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()+" Write "+key);TimeUnit.MICROSECONDS.sleep(300);map.put(key, value);System.out.println(Thread.currentThread().getName()+"Finished Writing");} 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()+" Read "+key);TimeUnit.MICROSECONDS.sleep(300);result = map.get(key);System.out.println(Thread.currentThread().getName()+" Finished Reading "+key);} catch (InterruptedException e) {e.printStackTrace();} finally {rwLock.readLock().unlock();}return result;}
}
public class ReadWriteLockDemo {public static void main(String[] args) {MyCache cache = new MyCache();// 创建线程放数据for (int i = 0; i < 5; i++) {final int num = i;new Thread(()->{cache.put(num+"",num+"");},String.valueOf(i)).start();}// 创建线程取数据for (int i = 0; i < 5; i++) {final int num = i;new Thread(()->{cache.get(num+"");},String.valueOf(i)).start();}}
}

如果不加锁,就会产生读锁或者写锁没有释放就会有其他类型的操作线程同时获取资源,非常不安全。

读写锁:一个资源可以被多个读线程访问,或者被一个写线程访问,不能同时存在两个类型的线程。

读写锁的特点:

  1. 多个读锁:当没有线程持有写锁时,多个读锁可以同时持有,这样可以提高读操作的并发性。

  2. 独占写锁:写锁是独占的,即在任何时候只能有一个线程持有写锁。如果一个线程持有写锁,其他线程的读锁和写锁请求都会被阻塞。

  3. 锁降级:持有写锁的线程释放锁后可以获取读锁,这是安全的,因为写锁的释放意味着对共享资源的修改已经完成,并且其他线程可以看到最新的状态。

  4. 锁升级:持有读锁的线程尝试获取写锁是不安全的,因为如果允许这样做,可能会导致写锁长时间得不到满足,从而降低性能或导致死锁。

读写锁的问题:

  1. 写饥饿:如果读锁频繁被获取,写锁可能会长时间得不到满足,导致写操作饥饿。

  2. 锁升级问题:如前所述,锁升级可能导致死锁或性能问题。

  3. 公平性ReentrantReadWriteLock提供了公平和非公平两种模式。在公平模式下,锁的分配会按照请求的顺序进行,这可能会降低性能。非公平模式下,性能可能更好,但可能导致饥饿现象。

  4. 死锁:如果不正确地使用读写锁,尤其是在尝试升级读锁到写锁时,可能会导致死锁。

public static void main(String[] args) {ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//锁降级---可以正常进行writeLock.lock();System.out.println("write");readLock.lock();System.out.println("read");writeLock.unlock();readLock.unlock();// 锁升级---死锁readLock.lock();System.out.println("read");writeLock.lock();System.out.println("write");// 无法执行readLock.unlock();writeLock.unlock();
}

BlockingQueue阻塞队列

阻塞队列,通过一个共享队列,可以使得数据由队列的一端输入,从另外一端输出。

 

  • ArrayBlockingQueue(常用):基于数组的阻塞队列实现,维护了一个定长的数据以便缓存队列中的数据对象,保存两个整型变量,标识队列的头部和尾部在数组中的位置。由数组结构组成的有界阻塞队列

  • LinkedBlockingQueue(常用):基于链表的阻塞队列,维护一个由链表构成的数据缓冲队列。大小默认为Integer.MAX_VALUE

  • DelayQueue:只有当其指定的延迟时间到了,才能从队列中获取到该元素,是一个没有大小限制的队列。使用优先级队列实现的延迟无界队列。

常用方法:

 

public static void main(String[] args) throws InterruptedException {// 创建阻塞队列BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);// 会抛出异常的方法System.out.println(blockingQueue.add("a"));// trueSystem.out.println(blockingQueue.add("b"));// trueSystem.out.println(blockingQueue.add("c"));// trueSystem.out.println(blockingQueue.element());// a//        System.out.println(blockingQueue.add("d"));// IllegalStateException: Queue fullSystem.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());//        System.out.println(blockingQueue.remove());// NoSuchElementException// 会返回特殊值的方法System.out.println(blockingQueue.offer("a"));// trueSystem.out.println(blockingQueue.offer("b"));// trueSystem.out.println(blockingQueue.offer("c"));// trueSystem.out.println(blockingQueue.offer("d"));// falseSystem.out.println(blockingQueue.poll());// aSystem.out.println(blockingQueue.poll());// bSystem.out.println(blockingQueue.poll());// cSystem.out.println(blockingQueue.poll());// null// 阻塞blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");// 阻塞System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());//阻塞}

 

volatile关键字

在Java中,volatile是一个关键字,用于修饰变量。它主要有以下几个作用:

  1. 保证可见性:当一个变量被声明为volatile时,它保证了对该变量的写操作对所有线程都是立即可见的。也就是说,当一个线程修改了一个volatile变量时,新值会立即被写入主内存,而其他线程在访问这个变量时,都会从主内存中读取最新的值。

  2. 禁止指令重排序volatile关键字可以防止编译器和处理器对读写该变量的指令进行重排序优化。在没有volatile修饰的情况下,编译器和处理器可能会为了优化性能而改变指令的执行顺序,这可能会导致一些线程安全问题。

  3. 不保证原子性:尽管volatile可以保证变量的可见性和防止指令重排序,但它并不保证复合操作的原子性。例如,对于i++这样的操作,即使ivolatile类型的,也不能保证在多线程环境下的线程安全。

volatile关键字通常用于以下场景:

  • 状态标志:用于表示某个状态的变化,比如一个线程完成工作的标志。

  • 双重检查锁定(Double-Checked Locking):在创建对象实例时,用于确保实例只被创建一次。

使用volatile时需要注意的是,它并不能完全替代synchronizedLock等同步机制,因为volatile不提供原子性保证,所以在需要原子性操作时,仍然需要使用其他同步机制。

ThreadPool线程池

线程池是一种线程使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,避免了在处理短时间任务时创建与销毁线程的代价。保证内核的充分利用,还能防止过度调度。

Java的线程池通过Executor框架实现的,使用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这些类

 

使用方式

  • Executors.newFixedThreadPool(int)一池N线程

  • Executors.newSingleThreadExecutor()任务一个一个执行,一池一线程

  • Executors.newCachedThreadPool()线程池根据需要创建线程,可扩容

// 线程池三种常见的分类
public static void main(String[] args) {// 一池N线程//        ExecutorService pool = Executors.newFixedThreadPool(5);//        try {//            // 需要处理10个请求//            for (int i = 0; i < 10; i++) {//                // 执行//                pool.execute(()->{//                    System.out.println(Thread.currentThread().getName());//                });//            }//        } finally {//            // 关闭线程池//            pool.close();//        }// 一池一线程//        try (ExecutorService pool1 = Executors.newSingleThreadExecutor()) {//            for (int i = 0; i < 10; i++) {//                pool1.execute(() ->//                        System.out.println(Thread.currentThread().getName())//                );//            }//        }// 可扩容线程try (ExecutorService pool2 = Executors.newCachedThreadPool()) {for (int i = 0; i < 20; i++) {pool2.execute(() -> {System.out.println(Thread.currentThread().getName());});}}}

线程池的底层原理

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}

 

  • int corePoolSize:核心(常驻)线程数量

  • int maximumPoolSize:最大线程数量

  • long keepAliveTime:线程存活时间(多开放的线程关闭)

  • TimeUnit unit:线程存活时间单位

  • BlockingQueue<Runnable> workQueue:常驻线程队列用完了,只能把任务放入到阻塞队列中

  • ThreadFactory threadFactory:线程工厂,创建线程

  • RejectedExecutionHandler handler:拒绝策略

工作流程和拒绝策略

 

线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,能够更加明确线程池的运行规则 

//自定义线程池创建
public class ThreadPoolDemo2 {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2,5,2L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());try {for (int i = 0; i < 10; i++) {executor.execute(()->{System.out.println(Thread.currentThread().getName()+" working");});}} finally {executor.close();}}
}

分支合并框架

Fork/Join可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。

Fork:把一个复杂任务进行分拆

Join:把分拆任务的结果进行合并 

CompletableFuture异步回调

public class CompletableFutureDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {// 异步调用 没有返回值CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{System.out.println(Thread.currentThread().getName()+" completableFuture1");});completableFuture1.get();// 异步调用,有返回值CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName()+" completableFuture2");int i = 1/0;return 1024;});completableFuture2.whenComplete((a,b)->{System.out.println("---a="+a); // supplyAsync 返回值System.out.println("---b="+b); // supplyAsync 异常}).get();}
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

调用matlab用户自定义的function函数时,有多个输出变量只输出第一个变量

很多朋友在使用matlab时&#xff0c;会使用或自己编辑多个function函数&#xff0c;来满足自己对任务处理的要求&#xff0c;但是在调用function函数时&#xff0c;会出现这个问题&#xff1a;调用matlab用户自定义的function函数时&#xff0c;有多个输出变量只输出第一个变量…

计算机毕设-基于springboot的志愿者招募管理系统的设计与实现(附源码+lw+ppt+开题报告)

博主介绍&#xff1a;✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围&#xff1a;Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

嵌入式蓝桥杯学习7 产生PWM

Cubemx配置 打开cubemx&#xff0c;前面的配置看上文&#xff0c;这里主要配置定时器产生PWM波。 以PA1的TIM2-CH2通道为例进行演示。 1.在Timers中打开TIM2,将Channel2配置为PWM Generation CH2。 2.将Clock Source 选择为Internal Clock。 3.配置Paramater Settings中的参…

LobeChat-46.6k星!顶级AI工具集,一键部署,界面美观易用,ApiSmart 是你肉身体验学习LLM 最好IDEA 工具

LobeChat LobeChat的开源&#xff0c;把AI功能集合到一起&#xff0c;真的太爽了。 我第一次发现LobeChat的时候&#xff0c;就是看到那炫酷的页面&#xff0c;这么强的前端真的是在秀肌肉啊&#xff01; 看下它的官网&#xff0c;整个网站的动效简直闪瞎我&#xff01; GitH…

【分子材料发现】——GAP:催化过程中吸附构型的多模态语言和图学习(数据集处理详解)(二)

Multimodal Language and Graph Learning of Adsorption Configuration in Catalysis https://arxiv.org/abs/2401.07408Paper Data: https://doi.org/10.6084/m9.figshare.27208356.v2 1 Dataset CatBERTa训练的文本字符串输入来源于Open Catalyst 2020 &#xff08;OC20…

[小白系列]Ubuntu安装教程-安装prometheus和Grafana

Docker安装prometheus 拉取镜像 docker pull prom/prometheus 配置文件prometheus.yml 在/data/prometheus/建立prometheus.yml配置文件。&#xff08;/data/prometheus/可根据自己需要调整&#xff09; global:scrape_interval: 15s # By default, scrape targets ev…

oracle之用户的相关操作

&#xff08;1&#xff09;创建用户(sys用户下操作) 简单创建用户如下&#xff1a; CREATE USER username IDENTIFIED BY password; 如果需要自定义更多的信息&#xff0c;如用户使用的表空间等&#xff0c;可以使用如下&#xff1a; CREATE USER mall IDENTIFIED BY 12345…

Jenkins环境一站式教程:从安装到配置,打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1

文章目录 Jenkins环境一站式教程&#xff1a;从安装到配置&#xff0c;打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1一、环境准备1.1 机器规划1.2 环境配置1.2.1 设置主机名1.2.2 停止和禁用防火墙1.2.3 更新系统 二、安装配置Jenkins2.1 安装JDK…

flinkSql 将流和表的互相转换

流——>表 方式一 方式二 方式一&#xff1a;写sql DataStreamSource<String> source env.socketTextStream("localhost", 8881); // 表名&#xff0c;流&#xff0c;字段名称 tableEnv.createTemporaryView("t_1",source&#xff0c;$("…

AI大模型驱动数据分析:利用自然语言实现数据查询与可视化(1)

在当今AI驱动的时代&#xff0c;数据分析已成为各行各业不可或缺的能力。然而&#xff0c;传统的数据分析流程通常需要掌握SQL、数据处理和可视化等多项专业技能&#xff0c;这对非技术背景的业务人员来说是一个不小的挑战。 想象一下&#xff0c;当数据中心的负责人打开手机时…

PyCharm+Selenium+Pytest配置小记

1、下载ChromeDriver&#xff1a; Chrome130以后的Driver下载&#xff1a; Chrome for Testing availabilityhttps://googlechromelabs.github.io/chrome-for-testing/ &#xff08;1&#xff09;查看自己Crome浏览器的版本&#xff1a;设置-->关于 Chrome&#xff1b; &…

【原生js案例】webApp实现鼠标移入移出相册放大缩小动画

图片相册这种动画效果也很常见&#xff0c;在我们的网站上。鼠标滑入放大图片&#xff0c;滑出就恢复原来的大小。现在我们使用运动定时器来实现这种滑动效果。 感兴趣的可以关注下我的系列课程【webApp之h5端实战】&#xff0c;里面有大量的css3动画效果制作原生知识分析&…

Qt 安装Qt Serial Port

最近要用Qt写个串口上位机软件&#xff0c;发现Qt的串口库用不了&#xff0c;上网找了一下资料&#xff0c;找到一种解决办法&#xff0c;具体操作如下&#xff1a; 参考文章&#xff1a;https 目录 一、找到QT安装路径&#xff0c;并运行Qt Maintenance Tool二、选择 添加或移…

语音识别flask接口开发

要开发一个flask语音识别接口&#xff0c;首先要解决语音文件在网络中的传输问题&#xff0c;然后选识别算法进行识别 文章目录 1、以二进制文件流方式上次语音2、网页端长连接流式上传语音文件3、语音识别接口 1、以二进制文件流方式上次语音 python服务端代码&#xff0c;以…

计算机毕业设计Python医疗问答系统 医疗可视化 BERT+LSTM+CRF深度学习识别模型 机器学习 深度学习 爬虫 知识图谱 人工智能 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

shell条件测试

一.命令执行结果判定 && 在命令执行后如果没有任何报错时会执行符号后面的动作 || 在命令执行后如果命令有报错会执行符号后的动作 示例&#xff1a; [rootqingdeng shell3]# sh sl.sh /mnt/file is not exist no二.条件判断方法 在 shell 程序中&#xff0c;用户可…

Couchbase Lite for Android 开源项目 FAQ

Couchbase Lite for Android 开源项目 FAQ couchbase-lite-android couchbase/couchbase-lite-android: Couchbase Lite for Android 是一个轻量级的嵌入式NoSQL数据库引擎&#xff0c;可以在Android设备上离线存储和处理数据&#xff0c;并支持与Couchbase Server进行同步&…

DVWA 靶场 SQL 注入报错 Illegal mix of collations for operation ‘UNION‘ 的解决方案

在 dvwa 靶场进行联合 SQL 注入时&#xff0c;遇到报错 Illegal mix of collations for operation UNION报错如下图&#xff1a; 解决办法&#xff1a; 找到文件MySQL.php 大致位置在dvwaincludesDBMS 目录下 使用编辑器打开 检索$create_db 第一个就是 在{$_DVWA[ ‘db_d…

使用伪装IP地址和MAC地址进行Nmap扫描

使用伪装IP地址和MAC地址进行Nmap扫描 在某些网络设置中&#xff0c;攻击者可以使用伪装的IP地址甚至伪装的MAC地址进行系统扫描。这种扫描方式只有在可以保证捕获响应的情况下才有意义。如果从某个随机的网络尝试使用伪装的IP地址进行扫描&#xff0c;很可能无法接收到任何响…

PT8M2102 触控型 8Bit MCU

1 产品概述 ● PT8M2102 是一款基于 RISC 内核的8位 MTP 单片机&#xff0c;内部集成了电容式触摸感应模块、TIMER&#xff0c;PWM、LVR、LVD、WDT等外设&#xff0c;其主要用作触摸按键开关&#xff0c;广泛适用于触控调光、电子玩具、消费电子、家用电器等领域&#xff0c;具…