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

JUC(java.util.concurrent) 的常见类

1.ReentrantLock

可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.

ReentrantLock 也是可重入锁. "Reentrant" 这个单词的原意就是 "可重入.
 

ReentrantLock 的用法:

  • lock(): 加锁, 如果获取不到锁就死等.
  • trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.
  • unlock(): 解锁
     
ReentrantLock lock = new ReentrantLock();-----------------------------------------lock.lock();try {// working} finally {lock.unlock();}

ReentrantLock 和 synchronized 的区别:

  • synchronized 是一个关键字, 是 JVM 内部实现的(大概率是基于 C++ 实现). ReentrantLock 是标准库的一个类, 在 JVM 外实现的(基于 Java 实现).
  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,但是也容易遗漏 unlock.
  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
  • 更强大的唤醒机制. synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程


 如何选择使用哪个锁?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

既然有了synchronized为啥还有ReentrantLock? 

ReentrantLock提供了更丰富的功能!!能做到synchronized做不到的事情!!

1.ReentrantLock 提供了一个公平锁的实现版本~~(构造方法中,通过一个标志位,指定是公平锁,还是非公平锁)

2.ReentrantLock提供了一个tryLock操作~~

  • trylock 能够指定一个加锁的等待时间.
  • synchronized如果拿不到锁,就死等..
  • trylock 能指定一个最大的等待时间~~


 

2.原子类
 

原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个

  • AtomicBoolean
  • AtomicInteger
  • AtomicIntegerArray
  • AtomicLong
  • AtomicReference
  • AtomicStampedReference
     

以 AtomicInteger 举例,常见方法有

addAndGet(int delta);  i += delta;
decrementAndGet();     --i;
getAndDecrement();     i--;
incrementAndGet();     ++i;
getAndIncrement();     i++;


3.线程池

虽然创建销毁线程比创建销毁进程更轻量, 但是在频繁创建销毁线程的时候还是会比较低效.
线程池就是为了解决这个问题. 如果某个线程不再使用了, 并不是真正把线程释放, 而是放到一个 "池子"中, 下次如果需要用到线程就直接从池子中取, 不必通过系统来创建了

ExecutorService 和 Executors

  • ExecutorService 表示一个线程池实例.
  • Executors 是一个工厂类, 能够创建出几种不同风格的线程池.
  • ExecutorService 的 submit 方法能够向线程池中提交若干个任务.
ExecutorService pool = Executors.newFixedThreadPool(10);pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});

 

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

Executors 本质上是 ThreadPoolExecutor 类的封装

ThreadPoolExecutor

ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定

理解 ThreadPoolExecutor 构造方法的参数

把创建一个线程池想象成开个公司. 每个员工相当于一个线程

  • corePoolSize: 正式员工的数量. (正式员工, 一旦录用, 永不辞退)
  • maximumPoolSize: 正式员工 + 临时工的数目. (临时工: 一段时间不干活, 就被辞退).
  • keepAliveTime: 临时工允许的空闲时间.
  • unit: keepaliveTime 的时间单位, 是秒, 分钟, 还是其他值.
  • workQueue: 传递任务的阻塞队列
  • threadFactory: 创建线程的工厂, 参与具体的创建线程工作.
  • RejectedExecutionHandler: 拒绝策略, 如果任务量超出公司的负荷了接下来怎么处理.
  • AbortPolicy(): 超过负荷, 直接抛出异常.
  • CallerRunsPolicy(): 调用者负责处理
  • DiscardOldestPolicy(): 丢弃队列中最老的任务.
  • DiscardPolicy(): 丢弃新来的任务.

 代码示例:

ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 3; i++) {pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});


信号量 Semaphore

信号量, 用来表示 "可用资源的个数". 本质上就是一个计数器.


理解信号量


可以把信号量想象成是停车场的展示牌: 当前有车位 100 个. 表示有 100 个可用资源.
当有车开进去的时候, 就相当于申请一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作)
当有车开出来的时候, 就相当于释放一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.
 

Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用.

代码示例:

  • 创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.
  • acquire 方法表示申请资源(P操作), release 方法表示释放资源(V操作)
  • 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执行效果.
package thread2;import java.util.concurrent.Semaphore;public class Test10 {public static void main(String[] args) {Semaphore semaphore = new Semaphore(4);Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("申请资源");semaphore.acquire();System.out.println("我获取到资源了");Thread.sleep(1000);System.out.println("我释放资源了");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}};for (int i = 0; i < 20; i++) {Thread t = new Thread(runnable);t.start();}}
}

 

CountDownLatch

同时等待 N 个任务执行结束.
好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩。

package thread4;import java.util.concurrent.CountDownLatch;public class Test1 {public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {Thread t = new Thread(() -> {try {Thread.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("选手撞线了!" + Thread.currentThread().getName());countDownLatch.countDown();});t.start();}countDownLatch.await();System.out.println("比赛结束!");}
}


线程同步的方式有哪些?

synchronized, ReentrantLock, Semaphore 等都可以用于线程同步
 

为什么有了 synchronized 还需要 juc 下的 lock?

以 juc 的 ReentrantLock 为例,

  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,
  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个true 开启公平锁模式.
  • synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.

AtomicInteger 的实现原理是什么?

基于 CAS 机制. 伪代码如下:

class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while (CAS(value, oldValue, oldValue + 1) != true) {oldValue = value;}return oldValue;}}

信号量听说过么?之前都用在过哪些场景下?

信号量, 用来表示 "可用资源的个数". 本质上就是一个计数器.
使用信号量可以实现 "共享锁", 比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作作为
加锁, V 操作作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进行 P 操作就会阻塞等待,
直到前面的线程执行了 V 操作.

解释一下 ThreadPoolExecutor 构造方法的参数的含义




 

 



 





 





 

http://www.lryc.cn/news/4055.html

相关文章:

  • Angular4 中 ckeditor5 插件的使用
  • [python刷题模板] 前缀函数/next数组/kmp算法
  • rust 程序设计语言入门(1)
  • 基于蜣螂算法改进的LSTM预测算法-附代码
  • Python安全开发——Scapy流量监控模块watchdog
  • 阶段二5_集合ArrayList
  • 十一、Python——匿名函数
  • 数组常使用的方法
  • 2023华为软件测试笔试面试真题,抓紧收藏不然就看不到了
  • 洛谷2月普及组(月赛)
  • 【博学谷学习记录】超强总结,用心分享 | 架构师 Spring源码学习总结
  • Linux C/C++ timeout命令实现(运行具有时间限制)
  • 西湖论剑初赛web wp
  • 【YOLOv8/YOLOv7/YOLOv5系列算法改进NO.55】融入美团最新QARepVGG
  • Flutter Windows端打包并生成可安装文件流程
  • 凸优化学习:PART3凸优化问题(持续更新)
  • [ue4] 着色器绑定(Shader Binding)
  • Rust语言之迭代器
  • TreeSet 与 TreeMap And HashSet 与 HashMap
  • Java围棋游戏的设计与实现
  • 第七十三章 使用 irisstat 实用程序监控 IRIS - 使用选项运行 irisstat
  • 【博客619】PromQL如何实现Left joins以及不同metrics之间的复杂联合查询
  • Win11自定义电脑右下角时间显示格式
  • TrueNas篇-trueNas Scale安装
  • element表单搜索框与表格高度自适应
  • MySQL使用技巧整理
  • 七大设计原则之里氏替换原则应用
  • 1行Python代码去除图片水印,网友:一干二净
  • Connext DDS属性配置参考大全(2)
  • 一起Talk Android吧(第四百九十二回:精简版动画)