JUC中的所有类详解

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 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 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 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 list = new CopyOnWriteArrayList<>();

public void add(String value) {

list.add(value);

}

public List 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 {

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官方文档

如果你觉得这篇文章对你有帮助,不妨点赞、关注和收藏!你的支持是我继续创作的动力,也让我有更多机会分享更多优质内容。谢谢大家!

鲷鱼读diao还是chou?鱼和周合在一起念什么?|为什么屁股总是凉凉的?3个生理知识,男女都该看看