当前位置: 首页 > news >正文

JUCJUCJUC

内容回顾

1、Lock锁

ReentrantLock

  • 可重入性

  • 公平锁和非公平锁

  • 限时等待

  • ReentrantLock和synchronized区别

读写锁

  • 读锁共享锁,写锁独占锁

  • 锁降级

2、线程间通信

  • 虚假唤醒

3、并发容器类

  • ArrayList

-- Vector

-- Collections里面方法

-- CopyOnWrite

今天内容

1、并发容器类

HashSet

//Collections Set<String> set = Collections.synchronizedSet(new HashSet<>()); ​ //CopyOnWrite Set<String> set1 = new CopyOnWriteArraySet<>();

HashMap

  • HashMap底层结构:数组+链表+树

  • 解决线程安全问题:

//Collections Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); ​ //juc里面对象解决 Map<String, String> map1 = new ConcurrentHashMap<>();

ConcurrentHashMap底层原理(重点)

  • ConcurrentHashMap底层使用分段锁+CAS

  • 在jdk1.8之前,把数组按照固定大小划分为多个数据段segment,操作数据时候,对数据所在数据段segment进行加锁,底层使用ReentrantLock实现加锁和解锁的

  • 在jdk1.8开始,取消了segment概念,操作数据时候,对数据所在节点进行加锁(数组元素加锁)

  • CAS: 乐观锁(后面讲)

2、JUC三个常见工具类

  • 三个工具类底层使用AQS(后面讲到)

CountDownLatch(减少计数)

  • 实现倒计时效果

  • 比如发射火箭,10s倒计时,倒计时变成0,火箭发射了

new CountDownLatch(int count) //实例化一个倒计数器,count指定初始计数 countDown() // 每调用一次,计数减一 await() //等待,当计数减到0时,阻塞线程(可以是一个,也可以是多个)并行执行
public class CountDownLatchDemo { ​ public static void main(String[] args) throws InterruptedException { //创建CountDownLatch对象,设置倒计时初始值 CountDownLatch countDownLatch = new CountDownLatch(6); ​ //创建6个线程,模拟6个同学 for (int i = 1; i <=6; i++) { new Thread(()->{ ​ System.out.println(Thread.currentThread().getName()+",开始学习"); try { TimeUnit.SECONDS.sleep(new Random().nextInt(10)); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+",结束学习"); ​ //倒计时初始值-1 countDownLatch.countDown(); },"同学"+i).start(); } ​ //类似拦截的方法 countDownLatch.await(); ​ System.out.println("同学都离开了,值班同学锁门"); } }

CyclicBarrier(循环栅栏)

  • 正计时,多个线程相互等待达到某个共同点之后再一起继续执行

  • 比如:七龙珠

package com.atguigu.juc.util; ​ import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; ​ public class CyclicBarrierDemo { ​ public static void main(String[] args) throws Exception { //创建CyclicBarrier对象,设置屏障点,达到屏障点之后执行逻辑 CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> { System.out.println("集齐了7颗龙珠,召唤神龙!!!!"); }); //创建7个线程 for (int i = 1; i <=7; i++) { ​ new Thread(()->{ ​ try { System.out.println(Thread.currentThread().getName()+ "星龙珠集齐了"); ​ cyclicBarrier.await(); } catch (Exception e) { throw new RuntimeException(e); } },String.valueOf(i)).start(); } } }

Semaphore(信号量)

  • 可以控制同时访问的线程个数(限流)

  • 停车场固定3个车位

public Semaphore(int permits) // 构造方法,permits指资源数目(信号量) public void acquire() throws InterruptedException // 占用资源, public void release() // (释放)
package com.atguigu.juc.util; ​ import java.util.Random; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; ​ public class SemaphoreDemo { ​ public static void main(String[] args) { //创建Semaphore对象,设置资源数量 Semaphore semaphore = new Semaphore(3); ​ //创建多个线程 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()+",驶出停车场"); ​ //释放资源 semaphore.release(); } catch (Exception e) { throw new RuntimeException(e); } },String.valueOf(i)).start(); } } }

3、Callable接口

  • 使用Callable不能直接替换Runnable接口,因为Thread类里面不支持Callable类型,在Thread里面只能传递Runnable类型

package com.atguigu.juc.callable; ​ import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; ​ //runnable class Thread1 implements Runnable { ​ @Override public void run() { System.out.println(Thread.currentThread().getName()); } } ​ //callable class Thread2 implements Callable<Integer> { ​ @Override public Integer call() throws Exception { return 1024; } } ​ public class CallableDemo { ​ public static void main(String[] args) { //callable,不能创建线程的 //new Thread(new Thread2(),"atguigu02").start(); ​ ////runnable new Thread(new Thread1(),"atguigu01").start(); } }
  • 想办法Runnable 和 Callable建立关系

  • 因为Runnable 和 Callable没有直接关系,找到中间对象建立关系,中间对象意思是和Runnable有关系,和Callable也有关系

  • 使用FutureTask建立Runnable 和 Callable的关系

  • FutureTask是java.util.concurrent里面类

-- FutrueTask间接实现了Runnable接口,FutrueTask就是Runnable接口实现类

-- FutureTask构造方法参数是Callable类型

package com.atguigu.juc.callable; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //callable class Thread2 implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()); return 1024; } } public class CallableDemo { public static void main(String[] args) { //callable //new Thread(new Thread2(),"atguigu02").start(); //1 创建FutureTask对象,对象构造传递callable类型对象 FutureTask futureTask = new FutureTask(new Thread2()); //2 使用Thread类创建线程 //futureTask是runnable实现类 new Thread(futureTask,"abcd").start(); //get方法获取结果 System.out.println(futureTask.get()); } }

FutureTask特点

特点一:异步计算(表示一个可取消的异步计算任务)

类似于之前说的并行,多个任务一起执行,最终汇总

特点二:获取计算结果

调用FutureTask里面的get方法获取到结果

//get方法获取结果 System.out.println(futureTask.get());
  • 相同任务只会计算一次,后面复用之前结果

特点三:判断任务是否执行完成

//返回结果true任务执行完成,如果false任务执行中 boolean isDone = futureTask.isDone();

callable接口与runnable接口区别

  • 相同点:都是接口,都可以编写多线程程序,都采用Thread.start()启动线程

  • 不同点:

  1. 具体方法不同:runnable方法是run方法,callable方法是call方法

  2. Runnable的run方法没有返回值;Callable的call可以返回执行结果,是个泛型

  3. Callable接口的call()方法允许throws异常;Runnable的run()方法异常只能在内部消化,不能往上继续throws

4、阻塞队列(BlockingQueue)

  • 队列特点先进先出,队列有两个操作入队(向队列放元素)和出队(从队列取出元素)

  • 阻塞队列是一种特殊队列,简单理解:入队和出队在特定情况下暂停(阻塞)

被阻塞的情况主要有如下两种:

  1. 当队列了的时候,依然进行入队列操作

  2. 当队列了的时候,依然进行出队列操作

  • 在juc工具类有接口BlockingQueue,方便实现阻塞队列效果,一边向队列里面放元素,一边从队列取出元素

package com.atguigu.juc.queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { //创建有界阻塞队列 BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); // 第一组方法:add remove //如果队列满了,继续添加元素抛出异常 //如果队列为空,继续从队列取出元素抛出异常 // System.out.println(queue.add("a")); //入队,正常则返回true // System.out.println(queue.add("b")); // System.out.println(queue.add("c")); //System.out.println(queue.add("d")); //队列已满仍然入队,报异常 // System.out.println(queue.remove()); //出队第一个元素,返回出队元素 // System.out.println(queue.remove()); // System.out.println(queue.remove()); // System.out.println(queue.remove()); //队列已空仍然出队,报异常 //System.out.println(queue.element()); //队列已空仍然获取元素,报异常 // 第二组:offer poll //调用offer方法添加元素,如果队列满了,返回false //调用poll方法从队列取出元素,如果队列为空,返回null // System.out.println(queue.offer("a")); //入队,正常则返回true // System.out.println(queue.offer("b")); // System.out.println(queue.offer("c")); // System.out.println(queue.offer("d")); //队列已满仍然入队, 返回false // System.out.println(queue.poll()); //出队第一个元素,返回出队元素 // System.out.println(queue.poll()); // System.out.println(queue.poll()); // System.out.println(queue.poll()); //队列已空仍然出队,返回null // 第三组:put take //入队时候如果队列满了,进行入队处于阻塞状态 //出队时候,如果队列为空,进行出队处于阻塞状态 // queue.put("a"); //入队 // queue.put("b"); // queue.put("c"); //queue.put("d"); //队列已满仍然入队, 发生阻塞 // System.out.println(queue.take()); //出队第一个元素,返回出队元素 // System.out.println(queue.take()); // System.out.println(queue.take()); // System.out.println(queue.take()); //队列已空仍然出队,发生阻塞*/ // 第四组:offer poll //offer和poll方法设置阻塞时间,超过时间自动结束 System.out.println(queue.offer("a")); //入队,正常则返回true System.out.println(queue.offer("b")); System.out.println(queue.offer("c")); //System.out.println(queue.offer("d", 5, TimeUnit.SECONDS)); //队列已满仍然入队,超时返回false System.out.println(queue.poll()); //出队第一个元素,返回出队元素 System.out.println(queue.poll()); System.out.println(queue.poll()); System.out.println(queue.poll(5, TimeUnit.SECONDS)); //队列已空仍然出队,,超时返回null*/ } }

5、线程池

什么是线程池

  • 之前学习过连接池,德鲁伊连接池,SpringBoot内置连接池等等

  • 连接池优点:预先创建一些连接,把这些连接放到集合里面,每次使用连接可以从集合里面获取连接,使用完这个连接之后放回到集合里面,达到连接复用。一般来讲一个连接池具备可伸缩特性。

  • 现在学习线程池,特点和连接池类似的,目的达到线程复用

  • 创建线程有四种方式,使用线程池是最常用的方式

如何创建线程池

第一种方式 使用工具类Executors里面方法进行创建

第二种方式 使用ThreadPoolExecutor类自定义代码进行创建(常用)

Executors工具类

  • 直接调用Executors工具类里面静态方法,可以创建不同类型线程池

  • 一池N线程,一池一线程,一池可扩容线程等等

  • 调用execute方法和submit方法从线程池获取线程,执行任务

  • 调用Executors.newFixedThreadPool(3);这个方法时候特点:这个时候只是创建池,在这个池里面目前还没有线程,调用execute方法时候才在池里面创建线程

public class ExecutorsDemo { public static void main(String[] args) { //一池N线程 ExecutorService executorService1 = Executors.newFixedThreadPool(3); // executorService1.submit(() -> { // System.out.println(Thread.currentThread().getName() + ",任务执行..."); // return "atguigu"; // }); //一池一线程 ExecutorService executorService2 = Executors.newSingleThreadExecutor(); //一池可扩容 ExecutorService executorService3 = Executors.newCachedThreadPool(); for (int i = 1; i <=5; i++) { executorService3.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } //executorService3.shutdown(); } }

底层原理

  • 底层使用ThreadPoolExecutor类实现线程池,向这个类里面传递7个参数

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

第一个参数 corePoolSize: 核心线程数(常驻线程数、初始线程数)

第二个参数 maximumPoolSize: 最大线程数

第三个参数 keepAliveTime: 空闲线程存活时间值

第四个参数 TimeUnit: 空闲线程存活时间值

第五个参数 workQueue: 工作队列(阻塞队列)

第六个参数 ThreadFactory: 线程工厂

第七个参数 RejectedExecutionHandler: 拒绝策略

重要题目

如何设置核心线程数(按照什么规则进行设置)?

  • 根据具体业务场景进行设置,从线程角度考虑业务场景一般包含两种:IO密集型和CPU密集型

  • IO密集型:核心线程数2N 或者 2N+1

N就是几核的,比如电脑是4核,2N就是8

  • CPU密集型:核心线程数N+1

  • 最大线程数没有特别规则,一般经常这样约定,CPU密集型最大线程数是1.5N-3N

    CPU密集型最大线程数是3N-4N

总结

1、并发容器类

  • ArrayList、HashSet、HashMap

  • ConcurrentHashMap底层原理

2、JUC工具类

  • 减少计数

  • 循环栅栏

  • 信号量

3、Callable接口

  • Callable创建线程,使用中间对象FutureTask

  • FutureTask特性:异步计算、判断任务是否完成、获取返回结果get方法

  • Callable和Runnable区别

4、阻塞队列

  • 队列特点先进先出,队列有两个操作入队(向队列放元素)和出队(从队列取出元素)

  • 阻塞队列是一种特殊队列,简单理解:入队和出队在特定情况下暂停(阻塞)

被阻塞的情况主要有如下两种:

  1. 当队列了的时候,依然进行入队列操作

  2. 当队列了的时候,依然进行出队列操作

  • BlockingQueue

5、线程池(重要)

  • 优点

  • 线程池创建方式:Executors工具类和使用ThreadPoolExecutor类

  • Executors工具类创建线程池

  • 底层原理:ThreadPoolExecutor类七个参数

  • 如何设置核心线程数

http://www.jsqmd.com/news/796744/

相关文章:

  • 迪索共研|气体压缩机行业调研报告:最值得信赖、最靠谱、最具专业水准三重保障 - 品牌推荐大师1
  • 绝区零自动化助手:5分钟掌握全自动游戏任务管理
  • 2026年俄罗斯莫斯科狩猎渔业展Hunting and Fishing in Russia - 中国组团单位- 新天国际会展 - 新天国际会展
  • 如何选择UPS,UPS基础培训资料(专业、详细)
  • 2026年湘潭断桥铝门窗与系统阳光房定制完全指南 - 年度推荐企业名录
  • 2026年4家无人机电力巡检公司对比 运检升级选品指南 - 速递信息
  • 信电助 - 信创坐席盒 UB-B-XC 型号功能列表
  • OpenOCD实战:从源码编译到JTAG调试RISC-V平台
  • 2026国内习惯养成营TOP9!广东省广州等地营地训练场户外拓展黄埔领越特训营值得家长信赖 - 十大品牌榜
  • 2026年电力巡检深度评测:3家无人机电力巡检公司对比 - 速递信息
  • 拿下一台主机后该干嘛?超详细内网与域信息收集指北
  • Windows Cleaner终极指南:彻底告别C盘爆红的免费系统优化神器
  • 2026年玻璃吸热增强膜设备厂家推荐:森联智能装备的深度解析 - 深度智识库
  • Vivado ILA调试翻车实录:为什么我的波形死活出不来?从时钟不匹配说起
  • 防晒霜哪个好?这5款防晒清爽控油真的绝绝子 - 全网最美
  • 安平县美宏丝网制品:锌钢护栏全场景合规交付服务商 - 奔跑123
  • 突破性医学影像三维可视化:MRIcroGL如何重塑临床诊断与科研工作流
  • 2026年常州热缩管源头厂家深度横评:从汽车线束到轨道交通阻燃防护的完整选型指南 - 精选优质企业推荐官
  • B站缓存视频终极转换指南:3分钟将m4s文件无损转为通用MP4格式
  • 咸宁改灯首选|车悦汽车改灯:十年专业只做车灯,咸宁改灯店NO.1实至名归 - Reaihenh
  • 2026 国内超声波流量计 TOP10|技术实力 + 市场口碑选型指南 - 仪表人叶工
  • MySQL 如何正确实现“随机采样”
  • 2026年常州热缩管源头厂家深度横评:从新能源电池防护到轨道交通阻燃解决方案完全指南 - 精选优质企业推荐官
  • 2026国内独立能力营TOP9!广东广州等地教官团队训练场猎鹰战神特训营户外拓展口碑出众广受好评 - 十大品牌榜
  • 微信聊天记录导出终极方案:用WeChatExporter掌握你的数字记忆
  • 2026年常州中车阻燃网管厂家深度横评:昶力管业与高分子材料定制化解决方案全景指南 - 精选优质企业推荐官
  • 如何快速解密Widevine加密视频:3个简单步骤重新掌控你的数字内容
  • 数仓分层设计
  • 5分钟掌握DistroAV:零基础搭建专业网络视频传输系统
  • 2026年常州热缩管源头厂家深度横评:汽车线束、轨道交通与新能源电池防护直供指南 - 精选优质企业推荐官