JUC中的所有类详解
一、基础概念1. JUC简介2. JUC基本组成
二、常用组件详解1. 线程池(Executor、ExecutorService、ScheduledExecutorService)1.1 线程池的概念与作用1.2 如何使用线程池1.3 ScheduledExecutorService的使用方法
2. 同步工具2.1 ReentrantLock2.2 Semaphore2.3 CountDownLatch2.4 CyclicBarrier2.5 Phaser
3. 原子类3.1 AtomicInteger3.2 AtomicReference
4. 并发集合4.1 ConcurrentHashMap4.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
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
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
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
public void add(String value) {
list.add(value);
}
public List
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
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官方文档
如果你觉得这篇文章对你有帮助,不妨点赞、关注和收藏!你的支持是我继续创作的动力,也让我有更多机会分享更多优质内容。谢谢大家!