概述
Java并发工具是Java平台的一部分,旨在简化多线程程序的设计和开发。
这些工具包括同步器(如ReentrantLock)、并发集合(如ConcurrentHashMap)、原子变量(如AtomicInteger)和任务调度器(如ScheduledExecutorService)。
它们使开发者能够构建高效、可伸缩的应用程序,即使在多核处理器上也能良好运行。
基础概念
- 线程安全:当多个线程尝试同时访问某个资源时,确保一致性和完整性的一种属性。
- 同步器:一种机制,用于协调线程之间的协作。
- 并发集合:能够在多线程环境下安全使用的集合类。
- 原子变量:不可分割的操作,确保单一操作的原子性。
ReentrantLock
一个可重入的互斥锁,比内置锁(synchronized关键字)更灵活。
public void methodOne() {
try {
// 使用ReentrantLock
lock.lock();
try {
// 同步块
System.out.println("hello");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
上述同步同步代码块中的代码只能被单线程访问,单实例下不会出现线程安全问题。
CountDownLatch
一个计数器,等待其他线程完成一系列任务后才能继续。常用于主子线程任务协调,比如需要等子线程全部执行完成才执行主线程任务,比如常用的Excel文件导出。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
public class ExampleClass {
private final CountDownLatch latch = new CountDownLatch(1);
public void methodTwo() throws InterruptedException {
// 使用CountDownLatch
latch.await();
// 继续执行任务
}
}
CyclicBarrier
一组线程互相等待直到所有都到达某个点后再一起开始。
CyclicBarrier在Java的并发编程中非常有用,尤其是在需要固定数量的线程相互等待,以协调它们之间的执行顺序时。例如,在并行分解设计中,CyclicBarrier可以用于确保所有线程都完成其子任务后,再继续执行主任务。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
public class ExampleClass {
private final CyclicBarrier barrier = new CyclicBarrier(2);
public void methodThree() {
// 使用CyclicBarrier
try {
barrier.await();
// 所有线程到达屏障点后的动作
} catch (InterruptedException | BrokenBarrierException e) {
// 处理异常
}
}
}
Semaphore
允许多个线程进入受限区域的能力。
Java中的Semaphore是一个计数信号量,用于控制对多个资源的访问。它可以用于实现一些控制机制,比如限流、同步等。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
public class ExampleClass {
private final Semaphore semaphore = new Semaphore(1);
public void methodFour() {
// 使用Semaphore
if (semaphore.tryAcquire()) {
try {
// 可以执行的任务
} finally {
semaphore.release();
}
}
}
}
ConcurrentHashMap
线程安全的HashMap。ConcurrentHashMap线程安全的原因得益于它的锁分级策略,ConcurrentHashMap将整个散列表分为许多个桶(bucket),每个桶都由一个锁保护。当多线程访问不同的桶时,可以并发进行。
并且在数据结构上采用了红黑树的数据结构,当链表长度超过某个阈值(8)时,链表会转换为红黑树,提高了查询的性能。
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap map = new ConcurrentHashMap<>();
// 插入元素
map.put("one", 1);
map.put("two", 2);
// 获取元素
Integer value = map.get("one");
System.out.println("The value of 'one' is: " + value);
// 删除元素
map.remove("two");
// 遍历ConcurrentHashMap
for (String key : map.keySet()) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
}
}
CopyOnWriteArrayList
CopyOnWriteArrayList是Java并发包中的一个线程安全的ArrayList实现。它的设计原理是:当对其进行修改操作(add、set、remove等)时,会复制当前数组,并在新的数组上进行修改,然后再将原数组的引用指向新数组。
这种机制使得CopyOnWriteArrayList可以在无锁的情况下实现多线程的数据读取。
但是也是由于这个机制,使的CopyOnWriteArrayList只适用于读多写少的场景,如果是读少写多的场景,可能会产生内存溢出,这个大家使用的时候注意一下就好了。
其API基本和ArrayList一直,就不多做赘述了。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
public class ExampleClass {
private CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
public void addToList(Integer element) {
list.add(element);
}
}
AtomicInteger & AtomicReference
AtomicInteger 是 Java 并发包中的一个原子整数类,它提供了一种用原子方式读取和更新整数值的方法。
AtomicInteger 的实现依赖于 Unsafe 类,该类提供了硬件级别的原子操作,它之所以能够保证操作的原子性,得益于Java底层的CAS乐观锁,关于CAS乐观锁,后续有时间我再整理一篇文章专门讲解。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class ExampleClass {
private AtomicInteger atomicInt = new AtomicInteger(0);
private AtomicReference
任务调度器
Java提供了ScheduledExecutorService来安排周期性任务。
ScheduledExecutorService是Java中用于执行定时任务的服务。它允许你提交Runnable或Callable任务,并指定它们开始执行的时间。
以下是ScheduledExecutorService的一些核心方法:
- schedule(Runnable, long, TimeUnit):安排在指定延迟后执行的一次性操作。
- scheduleAtFixedRate(Runnable, long, long, TimeUnit):安排重复执行的定时任务,从初始延迟后开始,并且以指定的周期重复。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledTasks {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.ScheduledExecutorService(1);
Runnable task = () -> System.out.println("Running a scheduled task");
// 在延迟两秒后首次执行,之后每一秒执行一次
executor.scheduleAtFixedRate(task, 2, 1, TimeUnit.SECONDS);
// 在指定的时间后执行一次
executor.schedule(() -> System.out.println("Once-off task"), 5, TimeUnit.SECONDS);
}
}
总结
Java并发工具为多线程编程提供了强大的支持。它们减少了手动同步的需要,但仍然需要小心使用以避免死锁、竞态条件和其他并发问题。
务必熟悉每个工具的特定行为和限制,否则可能南辕北辙,所以代码中如果需要使用这些工具,需要搞清楚使用场景,谨慎小心的使用。
记住,尽管这些工具很方便,但它们并不是银弹;正确的设计和架构才是成功并发的关键。
本文暂时没有评论,来添加一个吧(●'◡'●)