文章目录 前言一、对象锁juc.locks包二、原子类三、四个常用工具类3.1 信号量 Semaphore3.2 CountDownLatch 总结 前言 博主个人社区:开发与算
博主个人社区:开发与算法学习社区
博主个人主页:Killing Vibe的博客
欢迎大家加入,一起交流学习~~
在Java中除了synchronized关键字可以实现对象锁之外,java.util.concurrent中的Lock接口也可以实现对象锁。
介绍一下这个lock锁的简要实现:
注:使用Lock接口需要显式的进行加锁和解锁操作。
我们可以使用Lock接口的实现子类ReentrantLock来进行加锁解锁:
ReentrantLock 可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.
ReentrantLock 的用法:
lock()
: 加锁,获取锁失败的线程进入阻塞状态,直到其他线程释放锁,再次竞争,死等。 trylock
(超时时间): 加锁, 获取锁失败的线程进入阻塞态,等待一段时间,时间过了若还未获取到锁恢复执行,放弃加锁,执行其他代码 unlock()
: 解锁synchronized和lock的区别:
synchronized
是Java的关键字, 由 JVM 实现,需要依赖操作系统提供的线程互斥原语(mutex),而Lock
标准库的类和接口,其中一个最常用的子类( ReentrantLock ,可重入锁),由Java本身实现的,不需要依赖操作系统。
synchronized
隐式的加锁和解锁,lock
需要显示进行加锁和解锁
synchronized
在获取锁失败的线程时,死等;lock
可以使用trylock
等待一段时间之后自动放弃加锁,线程恢复执行
synchronized
是非公平锁, ReentrantLock
默认是非公平锁. 可以通过构造方法传入一个 true
开启公平锁模式.
synchronized
不支持读写锁,Lock
子类ReentrantReadWriteLock
支持读写锁。
更强大的唤醒机制. synchronized
是通过 Object 的 wait / notify
实现等待-唤醒. 每次唤醒的是一个随机等待的线程.ReentrantLock
搭配 Condition
类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程
小结:
一般场景synchronized足够用了,需要用超时等待锁,公平锁,读写锁再考虑使用juc.lock
如何选择使用哪个锁?
原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个:
以 AtomicInteger 举例,常见方法有:
addAndGet(int delta); i += delta;decrementAndGet(); --i;getAndDecrement(); i--;incrementAndGet(); ++i;getAndIncrement(); i++;
juc包下一共有四个常用工具类:
信号量Semaphore就是一个计数器,表示当前可用资源的个数
关于信号量Semaphore有两个核心操作:
Semaphore 的PV加减操作都是原子性的,再多线程场景下可以直接使用
public static void main(String[] args) { // 在构造参数传入可用资源的个数 // 可用资源为6个 Semaphore semaphore = new Semaphore(6); Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "准备申请资源"); // P操作,每次申请两个资源 semaphore.acquire(2); System.out.println(Thread.currentThread().getName() + "获取资源成功"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "释放资源"); // V操作,默认释放一个占有的资源 semaphore.release(2); }catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 20; i++) { Thread t = new Thread(runnable,String.valueOf(i + 1)); t.start(); } }
有点类似于大号的join方法
调用await方法的线程需要等待其他线程将计数器减为0才能继续恢复执行。
public static void main(String[] args) throws InterruptedException { // 等待线程需要等待的线程数,必须等这10个子线程全部执行完毕再恢复执行 CountDownLatch latch = new CountDownLatch(10); Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(1000)); System.out.println(Thread.currentThread().getName() + "到达终点"); // 计数器 - 1 latch.countDown(); }catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 10; i++) { Thread t = new Thread(runnable,"运动员" + i + 1); t.start(); } // main线程就是裁判线程,需要等待所有运动员到底终点再恢复执行 // 直到所有线程调用countdown方法将计数器减为0继续执行 latch.await(); System.out.println("比赛结束~最终获胜的是鹏哥,有请冠军给大家高歌一首~"); }
至于CyclicBarrier和Exchanger在本篇就不多介绍,读者可以自行查阅一下官方文档进行仔细的学习~如果有问题可以私信博主,别忘了点赞收藏+关注哦!
来源地址:https://blog.csdn.net/qq_43575801/article/details/128057659
--结束END--
本文标题: JUC包(java.util.concurrent)下的常用子类
本文链接: https://lsjlt.com/news/407311.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0