JUC
Java.util.concurrent
包, 存放了并发编程相关的组件, 目的是更好的支持高并发任务 (多线程只是实现并发编程的一种具体方式 …)
ReentrantLock
- synchronized 对对象加锁, 保护临界资源
- ReentreatLock 使用 lock 方法和 unlock 方法,加锁对象是 ReentrantLock 的实例
核心方法
lock():
加锁, 获取不到锁就死等trylock(超时时间):
尝试加锁, 如果获取不到锁, 等待一段时间后就放弃加锁unlock():
解锁
ReentrantLock 使用
由于 reentreatLock 需要手动释放, 因此推荐
try finally
的写法
// ReentrantLock 使用 public class ThreadDemo12 { public static void main(String[] args) { // true -- 公平锁 false/默认 都是非公平锁 ReentrantLock reentrantLock = new ReentrantLock(true); boolean ok = reentrantLock.tryLock(); // boolean ok = reentrantLock.lock() try { if (ok) { System.out.println("代码逻辑"); }else { System.out.println("代码逻辑"); } }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) { Scanner scanner = new Scanner(System.in); int x = scanner.nextInt(); AtomicInteger atomicInteger = new AtomicInteger(x); 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秒之后输出 “任务全部完成”