手撕Java系列--java.util.concurrent包详解
java.util.concurrent 包提供了丰富的并发工具和数据结构,使得编写高性能、高可靠性的多线程应用程序变得更加容易。通过合理使用这些工具,可以显著提高程序的并发性能和可维护性。
Gitee链接地址,建议收藏,后续我会对专栏进行统一整理,每篇文章进行校正和调整,然后统一存放在gitee仓库中
综述
这个包主要包含以下几大类别的内容:
1. 并发集合
java.util.concurrent 包提供了多个线程安全的集合类,这些集合类在高并发环境下表现更好,避免了使用传统的同步机制(如synchronized关键字)带来的性能瓶颈。
- ConcurrentHashMap:线程安全的哈希表实现,支持高并发读写操作。
- CopyOnWriteArrayList:线程安全的列表实现,每次修改都会复制整个列表,适用于读多写少的场景。
- CopyOnWriteArraySet:基于
CopyOnWriteArrayList的线程安全的集合实现。 - ConcurrentLinkedQueue:无界线程安全队列,基于链表实现。
- ConcurrentSkipListMap 和 ConcurrentSkipListSet:线程安全的跳表实现,支持高效的查找、插入和删除操作。
- BlockingQueue 接口及其实现类(如
ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue,SynchronousQueue):线程安全的阻塞队列,适用于生产者-消费者模式。
2. 同步工具
java.util.concurrent 包提供了一些高级的同步工具,帮助解决复杂的并发问题。
- Semaphore:信号量,用于控制同时访问某个资源的线程数量。
- CountDownLatch:倒计时锁存器,允许一个或多个线程等待其他线程完成操作。
- CyclicBarrier:循环屏障,允许一组线程互相等待到达一个共同的屏障点。
- Exchanger:交换器,允许两个线程在某个汇合点交换数据。
- Phaser:可重用的同步屏障,类似于
CyclicBarrier,但更加灵活,支持动态注册和注销参与者。
3. 执行框架
java.util.concurrent 包提供了一个强大的执行框架,用于管理和调度任务。
- ExecutorService:执行服务接口,提供了一种将任务提交与任务执行解耦的方式。
- ThreadPoolExecutor:线程池实现,允许复用已存在的线程,提高性能。
- ScheduledExecutorService:支持定时和周期性任务的执行服务。
- ForkJoinPool:分叉/合并框架的线程池实现,适用于可以分解为子任务的计算密集型任务。
- Future 和 CompletableFuture:表示异步计算的结果,支持回调和组合操作。
4. 原子变量
java.util.concurrent.atomic 包提供了一系列原子操作类,这些类使用硬件级别的原子指令实现,性能非常高。
- AtomicBoolean:原子布尔值。
- AtomicInteger 和 AtomicLong:原子整数和长整数。
- AtomicReference:原子引用。
- AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray:原子数组。
- AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater:用于更新对象字段的原子更新器。
5. 锁和条件
java.util.concurrent.locks 包提供了一系列锁和条件变量,用于更细粒度的同步控制。
- Lock 接口及其实现(如
ReentrantLock):可重入锁,支持公平和非公平模式。 - ReadWriteLock 接口及其实现(如
ReentrantReadWriteLock):读写锁,允许多个读线程同时访问,但写线程独占访问。 - Condition:条件变量,用于在锁上等待和通知线程。
示例代码
使用ExecutorService提交任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
使用CountDownLatch同步线程
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 3;
CountDownLatch latch = new CountDownLatch(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {
new Thread(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + " is starting");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + Thread.currentThread().getName() + " is done");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All threads have finished");
}
}
高级同步工具
java.util.concurrent 包中包含了许多高级的同步工具,这些工具可以帮助开发者解决复杂的并发问题。除了上面提到的 Semaphore, CountDownLatch, CyclicBarrier, Exchanger, 和 Phaser 之外,还有一些其他重要的同步工具和概念。以下是详细的介绍:
1. SynchronousQueue
SynchronousQueue 是一个特殊的阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作,反之亦然。这意味着每个元素的插入操作必须立即由另一个线程的移除操作匹配,否则会阻塞。
import java.util.concurrent.SynchronousQueue;
public class SynchronousQueueExample {
public static void main(String[] args) {
SynchronousQueue<String> queue = new SynchronousQueue<>();
new Thread(() -> {
try {
String message = queue.take();
System.out.println("Received: " + message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
queue.put("Hello");
System.out.println("Sent: Hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
2. Exchanger
Exchanger 是一个用于在两个线程之间交换数据的同步点。当两个线程都到达交换点时,它们可以交换数据。
import java.util.concurrent.Exchanger;
public class ExchangerExample {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String data = "Data from Thread 1";
System.out.println("Thread 1: Sending " + data);
String received = exchanger.exchange(data);
System.out.println("Thread 1: Received " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
String data = "Data from Thread 2";
System.out.println("Thread 2: Sending " + data);
String received = exchanger.exchange(data);
System.out.println("Thread 2: Received " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
3. Phaser
Phaser 是一个灵活的同步屏障,类似于 CyclicBarrier,但功能更强大。它可以动态地注册和注销参与者,并支持分阶段的同步。
import java.util.concurrent.Phaser;
public class PhaserExample {
public static void main(String[] args) {
Phaser phaser = new Phaser(3); // 初始参与者数量
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 到达并等待所有参与者
System.out.println("Thread 1: Phase 1 completed");
phaser.arriveAndAwaitAdvance(); // 下一个阶段
System.out.println("Thread 1: Phase 2 completed");
}).start();
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 到达并等待所有参与者
System.out.println("Thread 2: Phase 1 completed");
phaser.arriveAndAwaitAdvance(); // 下一个阶段
System.out.println("Thread 2: Phase 2 completed");
}).start();
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 到达并等待所有参与者
System.out.println("Thread 3: Phase 1 completed");
phaser.arriveAndAwaitAdvance(); // 下一个阶段
System.out.println("Thread 3: Phase 2 completed");
}).start();
}
}
4. LockSupport
LockSupport 提供了基本的线程阻塞和唤醒操作。它是许多同步工具(如 ReentrantLock 和 Semaphore)的基础。
import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread 1: Starting");
LockSupport.park();
System.out.println("Thread 1: Unparked");
});
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread: Unparking Thread 1");
LockSupport.unpark(t1);
}
}
5. StampedLock
StampedLock 是一个读写锁的高级实现,支持乐观读、悲观读和写操作。它比 ReentrantReadWriteLock 更灵活,性能也更好。
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private double x, y;
public void move(double deltaX, double deltaY) {
long stamp = lock.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp);
}
}
public double distanceFromOrigin() {
long stamp = lock.tryOptimisticRead();
double currentX = x, currentY = y;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
6. Condition
Condition 是 Lock 接口的一部分,用于在锁上等待和通知线程。它类似于 Object 的 wait 和 notify 方法,但更灵活。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final int[] items = new int[100];
private int putptr, takeptr, count;
public void put(int item) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = item;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public int take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
int item = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
总结
java.util.concurrent 包提供了丰富的高级同步工具,这些工具可以帮助开发者更有效地管理并发问题。通过合理选择和使用这些工具,可以显著提高多线程程序的性能和可靠性。后续我会用一系列文章详细介绍每一个工具的设计思路、实现原理和使用场景,帮助你在并发场景下解决更多问题。
更多推荐
所有评论(0)