JUC中的所有类详解
JUC中的所有类详解
- 一、基础概念
- 1. JUC简介
- 2. JUC基本组成
- 二、常用组件详解
- 1. 线程池(Executor、ExecutorService、ScheduledExecutorService)
- 1.1 线程池的概念与作用
- 1.2 如何使用线程池
- 1.3 ScheduledExecutorService的使用方法
- 2. 同步工具
- 2.1 ReentrantLock
- 2.2 Semaphore
- 2.3 CountDownLatch
- 2.4 CyclicBarrier
- 2.5 Phaser
- 3. 原子类
- 3.1 AtomicInteger
- 3.2 AtomicReference
- 4. 并发集合
- 4.1 ConcurrentHashMap
- 4.2 CopyOnWriteArrayList
- 三、常见实践
- 1. 使用线程池
- 2. 使用锁机制
- 3. 使用Fork/Join框架进行数据处理
- 四、最佳实践
- 1. 选择合适的并发工具
- 2. 减少锁争用
- 3. 避免死锁
- 五、底层原理
- 1. 线程池底层原理
- 2. 原子类底层实现
- 3. 并发集合底层实现
- 4. 同步工具底层实现
- 六、总结
- 七、参考资料
一、基础概念
1. JUC简介
Java Util Concurrent(JUC)是Java并发编程的核心库,它提供了丰富的工具和类来帮助开发者更高效地处理多线程问题。JUC的目标是简化并发编程的复杂性,提供经过验证的并发工具,让开发者能够专注于业务逻辑,而不是底层的线程同步和资源管理。
2. JUC基本组成
JUC主要由以下几个部分组成:
- 同步辅助类:如
ReentrantLock
、Semaphore
、CountDownLatch
等,用于解决线程同步和协调问题。 - 并发集合:如
ConcurrentHashMap
、CopyOnWriteArrayList
等,提供了线程安全的集合操作。 - 执行器框架:如
ExecutorService
、ScheduledExecutorService
等,用于管理和调度线程池。 - 原子变量:如
AtomicInteger
、AtomicReference
等,提供了线程安全的变量操作。
二、常用组件详解
1. 线程池(Executor、ExecutorService、ScheduledExecutorService)
1.1 线程池的概念与作用
线程池是一种用于管理和复用线程的机制,它可以有效降低线程创建和销毁的开销,提高程序的性能和资源利用率。
1.2 如何使用线程池
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
// 提交任务
executor.submit(() -> {
System.out.println("任务执行中...");
});
// 关闭线程池
executor.shutdown();
1.3 ScheduledExecutorService的使用方法
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 定时任务:每2秒执行一次
scheduler.scheduleAtFixedRate(() -> {
System.out.println("定时任务执行中...");
}, 0, 2, TimeUnit.SECONDS);
2. 同步工具
2.1 ReentrantLock
适用场景:解决多线程竞争资源的问题,如多线程同时对同一个数据进行写操作。
代码使用案例:通过生产者消费者模式演示ReentrantLock的使用。
public class ProducerConsumer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final List<Integer> list = new ArrayList<>();
private final int capacity = 10;
public void produce(int value) {
lock.lock();
try {
while (list.size() == capacity) {
notFull.await();
}
list.add(value);
notEmpty.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public Integer consume() {
lock.lock();
try {
while (list.isEmpty()) {
notEmpty.await();
}
Integer value = list.remove(0);
notFull.signal();
return value;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
源码分析:ReentrantLock基于AQS(AbstractQueuedSynchronizer)的实现原理,通过FIFO队列管理线程的获取和释放。
2.2 Semaphore
适用场景:实现服务接口限流、数据库连接池等。
代码使用案例:使用Semaphore限制并发访问的线程数。
public class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(3); // 最大允许3个线程同时访问
public void accessResource() {
try {
semaphore.acquire();
System.out.println("资源被访问,当前线程:" + Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
}
}
源码分析:Semaphore同样基于AQS实现,通过计数器管理资源的可用性。
2.3 CountDownLatch
适用场景:模拟百米赛跑、多任务完成后合并汇总。
代码使用案例:通过CountDownLatch实现多线程任务的同步。
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
// 启动3个线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("线程:" + Thread.currentThread().getName() + " 开始执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("线程:" + Thread.currentThread().getName() + " 执行完成");
latch.countDown();
}).start();
}
// 主线程等待所有子线程完成
latch.await();
System.out.println("所有线程执行完成");
}
}
源码分析:CountDownLatch内部维护一个计数器,通过countDown()
减少计数,await()
阻塞等待计数归零。
2.4 CyclicBarrier
适用场景:模拟人满发车、多线程批量处理数据。
代码使用案例:使用CyclicBarrier实现多线程任务的分阶段执行。
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程完成一个阶段,开始下一个阶段");
});
// 启动3个线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 开始执行");
Thread.sleep(1000);
barrier.await();
System.out.println("线程:" + Thread.currentThread().getName() + " 完成一个阶段");
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
源码分析:CyclicBarrier通过循环屏障机制实现多线程的分阶段同步。
2.5 Phaser
适用场景:多线程批量处理数据、阶段性任务。
代码使用案例:使用Phaser实现多阶段任务的同步。
public class PhaserExample {
public static void main(String[] args) {
Phaser phaser = new Phaser(3);
// 启动3个线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println("线程:" + Thread.currentThread().getName() + " 开始执行阶段1");
Thread.sleep(1000);
phaser.arriveAndAwaitAdvance(); // 完成阶段1,等待其他线程
System.out.println("线程:" + Thread.currentThread().getName() + " 开始执行阶段2");
Thread.sleep(1000);
phaser.arriveAndAwaitAdvance(); // 完成阶段2,等待其他线程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
phaser.arriveAndDeregister(); // 主线程完成注册
}
}
源码分析:Phaser通过阶段计数器管理多线程的同步,支持动态注册和注销。
3. 原子类
3.1 AtomicInteger
适用场景:实现线程安全的计数器。
代码使用案例:使用AtomicInteger进行线程安全的计数。
public class AtomicIntegerExample {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.getAndIncrement();
}
public int getCount() {
return count.get();
}
}
源码分析:AtomicInteger基于CAS(Compare-And-Swap)操作实现线程安全的原子操作。
3.2 AtomicReference
适用场景:实现线程安全的对象引用。
代码使用案例:使用AtomicReference进行线程安全的对象操作。
public class AtomicReferenceExample {
private AtomicReference<String> reference = new AtomicReference<>("初始值");
public void updateValue(String newValue) {
reference.set(newValue);
}
public String getValue() {
return reference.get();
}
}
源码分析:AtomicReference通过CAS操作实现线程安全的对象引用更新。
4. 并发集合
4.1 ConcurrentHashMap
适用场景:高并发场景下的键值存储。
代码使用案例:使用ConcurrentHashMap进行线程安全的Map操作。
public class ConcurrentHashMapExample {
private ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
public void put(String key, String value) {
map.put(key, value);
}
public String get(String key) {
return map.get(key);
}
}
源码分析:ConcurrentHashMap通过分段锁和哈希表扩展实现高并发的键值存储。
4.2 CopyOnWriteArrayList
适用场景:遍历操作远多于修改操作的场景。
代码使用案例:使用CopyOnWriteArrayList进行线程安全的List操作。
public class CopyOnWriteArrayListExample {
private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
public void add(String value) {
list.add(value);
}
public List<String> getAll() {
return list;
}
}
源码分析:CopyOnWriteArrayList通过写时复制机制实现线程安全的List操作。
三、常见实践
1. 使用线程池
通过实际案例演示如何使用线程池来提高程序的性能和资源利用率。
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
// 提交10个任务
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
System.out.println("任务执行中,线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
}
}
2. 使用锁机制
介绍如何使用ReentrantLock等锁机制来解决线程安全问题,并提供实际代码示例。
public class LockExample {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
3. 使用Fork/Join框架进行数据处理
讲解Fork/Join框架的原理和使用方法,并通过案例展示其在大数据处理中的优势。
public class ForkJoinExample extends RecursiveTask<Integer> {
private final int threshold = 2;
private int start;
private int end;
public ForkJoinExample(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= threshold) {
int sum = 0;
for (int i = start; i < end; i++) {
sum += i;
}
return sum;
} else {
int mid = (start + end) / 2;
ForkJoinExample left = new ForkJoinExample(start, mid);
ForkJoinExample right = new ForkJoinExample(mid, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinExample task = new ForkJoinExample(0, 100);
System.out.println(pool.invoke(task));
}
}
四、最佳实践
1. 选择合适的并发工具
根据不同的并发场景,分析如何选择最合适的并发工具来解决问题。
- 高并发读操作:使用
ConcurrentHashMap
。 - 高并发写操作:使用
CopyOnWriteArrayList
。 - 定时任务:使用
ScheduledExecutorService
。 - 线程安全计数:使用
AtomicInteger
。
2. 减少锁争用
提供一些减少锁争用的技巧和策略,提高程序的并发性能。
- 使用细粒度锁:如
ReentrantLock
的分段锁。 - 使用无锁数据结构:如
AtomicInteger
。 - 避免死循环中的锁操作:将锁操作放在循环外部。
3. 避免死锁
分析死锁产生的原因,并介绍如何通过设计和编码避免死锁的发生。
- 按顺序获取锁:确保所有线程按相同的顺序获取多个锁。
- 使用
tryLock
:尝试获取锁,避免无限期等待。 - 减少锁的持有时间:尽量缩短锁的持有时间。
五、底层原理
1. 线程池底层原理
深入分析线程池的底层实现,包括工作线程的创建、任务队列的管理以及拒绝策略的执行。
- 工作线程:线程池中的线程负责执行任务。
- 任务队列:存储待执行的任务。
- 拒绝策略:当任务队列满且线程池已满时,执行拒绝策略。
2. 原子类底层实现
讲解原子类的底层实现原理,如CAS操作等,帮助理解原子类的高效性和线程安全性。
- CAS操作:Compare-And-Swap,原子比较并交换。
- Unsafe类:底层操作内存地址的类。
3. 并发集合底层实现
详细分析并发集合的底层数据结构和算法,如ConcurrentHashMap的分段锁和哈希表扩展等。
- 分段锁:将哈希表分为多个段,每个段独立加锁。
- 哈希表扩展:动态扩展哈希表以适应更多数据。
4. 同步工具底层实现
讲解ReentrantLock、Semaphore等同步工具的底层实现原理,包括AQS(AbstractQueuedSynchronizer)的使用。
- AQS:AbstractQueuedSynchronizer,用于构建锁和同步器的框架。
- FIFO队列:管理线程的获取和释放顺序。
六、总结
JUC中的类为Java并发编程提供了强大的工具,通过合理选择和使用这些工具,可以有效解决多线程编程中的复杂问题。以下是一些关键点总结:
- 线程池:有效管理线程资源,提高性能。
- 同步工具:解决线程同步和协调问题。
- 原子类:提供高效的线程安全操作。
- 并发集合:支持高并发场景下的数据存储和操作。
七、参考资料
- JUC官方文档
如果你觉得这篇文章对你有帮助,不妨点赞、关注和收藏!你的支持是我继续创作的动力,也让我有更多机会分享更多优质内容。谢谢大家!