JUC
Java.util.concurrent
包, 存放了并发编程相关的组件, 目的是更好的支持高并发任务 (多线程只是实现并发编程的一种具体方式 …)
ReentrantLock
可重入互斥锁, 和 synchronized 定位类似, 用来实现互斥效果, 保证线程安全.
- synchronized 对对象加锁, 保护临界资源
- ReentreatLock 使用 lock 方法和 unlock 方法,加锁对象是 ReentrantLock 的实例
核心方法
lock():
加锁, 获取不到锁就死等trylock(超时时间):
尝试加锁, 如果获取不到锁, 等待一段时间后就放弃加锁unlock():
解锁
ReentrantLock 使用
由于 reentreatLock 需要手动释放, 因此推荐
try finally
的写法
public class ReentreatLockDemo {public static void main(String[] args) {ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();boolean ok = reentrantLock.tryLock();try {if(ok) {// do} else {// undo}} finally {reentrantLock.unlock();}}
}
ReentrantLock 和 synchronized 比较
- synchronized 是关键字, 是 JVM 内部实现的
ReentrantLock 是标准库的一个类, 在 JVM 外实现 (基于 Java 实现)
- synchronized 是非公平锁
ReentrantLock 默认是非公平锁, 但是提供了公平锁版本的实现
ReentrantLock reentrantLock = new ReentrantLock(true);
- ReentrantLock 提供更灵活的加锁方式:
ReentrantLock reentrantLock = new ReentrantLock(true);
reentrantLock.tryLock();
- ReentrantLock 提供更强大, 更方便的等待通知机制
synchronized
搭配wait()
notify()
使用,notify()
是随机唤醒等待队列的线程
ReentrantLock
搭配Condition
类. 可以唤醒指定的线程
原子类
原子类内部用的是 CAS 实现, 更高效的解决了
线程安全
问题
原子类提供了线程安全的自增自减
等操作
原子类有以下几种 :
原子类的常见方法 (以 AtomicInteger 为例)
public class Main {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger();Scanner scanner = new Scanner(System.in);int x = scanner.nextInt();atomicInteger.getAndIncrement();// i++;atomicInteger.incrementAndGet();// ++i;atomicInteger.getAndDecrement();// i--;atomicInteger.decrementAndGet();// --i;atomicInteger.addAndGet(x); // i+=x;atomicInteger.get(); // x}
}
线程池
之前写过, 挂个链接这里不再复制粘贴了 — https://editor.csdn.net/md/?articleId=136715895
信号量 Semaphore
信号量表示
"可用资源的个数"
.本质上是一个计数器
Semaphore 提供了 P,V 操作
P 操作: 申请一个可用资源, 计数器 - 1
V 操作: 释放一个可用资源, 计数器 + 1
当可用资源个数为 0 时, 再进行 P 操作, 就会出现阻塞等待清空 (资源为零, 无法继续消耗了), 直到有线程让信号量大于零, 才会唤醒该阻塞的线程
锁 可可以视为计数器为 1 的信号量, 二元信号量
- 锁是信号量的一种特殊情况
- 信号量是锁的一般表达
总结: 信号量的表达含义范围更广
Semaphore 的简单使用
代码示例
public class Main {public static void main(String[] args) {// 参数是可用资源的个数(信号量的初始值)Semaphore semaphore = new Semaphore(4);for (int i=0;i<20;i++) {Thread t = new Thread(() -> {try {System.out.println("申请资源");semaphore.acquire();System.out.println("持有资源");Thread.sleep(1000);System.out.println("释放资源");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}});t.start();}}
}
运行结果
有兴趣可以仔细看看运行结果,
同一时刻最多只有 4个线程能够持有锁
, 这就是信号量的存在意义
CountDownLatch
同时等待 N 个任务执行结束 (和 join() 功能类似)
核心API
- await(): 阻塞等待线程, 直至任务全部完成
- getCount(): 获取剩余未完成任务个数
- countDown(): 未完成任务个数 -1
代码示例
public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {// 参数代表需要等待的任务数量CountDownLatch countDownLatch = new CountDownLatch(5);for (int i = 0; i < 5; i++) {Thread t = new Thread(() -> {System.out.println("完成一个任务");// countDown() 方法, 代表完成一个任务countDownLatch.countDown();});t.start();Thread.sleep(1000);}// await()方法, 用于阻塞线程// 直至 countDownLatch 内任务全部完成, 才会往下继续走countDownLatch.await();System.out.println("任务全部完成");}
}
运行结果
运行过代码会发现, 每间隔一秒输出一次 “完成一个任务”, 5秒之后输出 “任务全部完成”