java.util.concurrent包是 Java 中用于并发编程的重要工具集,提供了线程池、原子变量、并发集合、同步工具类、阻塞队列等一系列高级并发工具类,使用这些工具类可以极大地简化并发编程的难度,减少出错的可能性,提高程序的效率和可维护性。
官方文档地址:https://docx.iamqiang.com/jdk11/api/java.base/java/util/concurrent/package-summary.html
Executor Framework是Java并发编程中一个非常强大的组件,它提供了一种标准的方法来启动、管理和控制线程的执行,执行器框架主要由接口和类组成,如Executor、Executors、ExecutorService、Future和Callable,这些组件共同协作,提供了一种灵活且高效的线程管理机制。
以下是关于执行器框架中一些关键组件的说明:
下面是一个使用执行器框架的简单示例,展示了如何创建一个固定大小的线程池,提交任务,并处理返回结果,如下代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ExecutorFrameworkExample {
public static void main(String[] args) {
// 创建一个固定大小为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务并获取Future对象
Future<String> future = executor.submit(() -> {
// 模拟耗时操作
Thread.sleep(1000);
return "任务完成";
});
// 做其他事情...
// 获取任务结果(如果任务还未完成,会阻塞等待)
try {
String result = future.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
}
}
在实际应用中,通常不会在线程池刚提交任务后就立即关闭它,而是会等待所有任务都提交完毕后再关闭,此外,future.get()
方法会阻塞当前线程直到任务完成,因此在需要等待任务完成的场景下应该谨慎使用,以避免死锁或不必要的线程阻塞。执行器框架通过提供这些高级抽象,使得开发者能够更专注于任务的逻辑,而不用过多关心线程的创建、调度和管理等底层细节,从而极大地简化了并发编程的复杂性。
Concurrent Collections是设计用于支持并发编程的一组数据结构,在并发编程中,当多个线程同时访问和修改共享数据时,如果没有适当的同步措施,就可能导致数据不一致和其他并发问题,为了避免这些问题,Java提供了一些线程安全的集合类,称为并发集合。并发集合位于java.util.concurrent包中,它们通过内部实现来确保多个线程可以安全地并发访问这些集合,而无需在客户端代码中进行额外的同步,这些集合使用了各种复杂的算法和数据结构来最小化线程间的竞争,从而提供更高的吞吐量。
以下是一些常见的并发集合类:
并发集合的使用场景主要是在多线程环境中,当需要一个数据结构来安全地共享数据时,使用这些集合可以减少编写和维护复杂的同步代码的需要,同时提高程序的性能和可伸缩性。
如下是使用ConcurrentHashMap代码示例,如下代码:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentCollectionExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 多个线程可以安全地并发访问这个 map
map.put("apple", 1);
map.put("banana", 2);
System.out.println(map.get("apple")); // 输出 1
}
}
ConcurrentHashMap是一种高效的线程安全的哈希表实现,它允许多个线程并发地读写数据,而不需要额外的同步。
Atomic Variables(原子变量)是并发编程中用于实现线程安全的一种机制,原子变量提供了一种在多线程环境中安全地读取、修改和更新变量的方式,而不需要额外的同步措施。在并发编程中,当多个线程同时访问和修改共享变量时,如果没有适当的同步措施,可能会导致数据不一致和其他并发问题,为了解决这个问题,通常需要使用锁或其他同步机制来确保对变量的访问是原子的。
原子变量提供了一种更简洁、高效的方式来实现线程安全的数据更新,它们通过内部机制来确保对变量的操作是原子的,从而避免了显式的同步,原子变量通常在底层使用硬件支持或特殊的指令来实现原子操作,这使得它们在性能上优于传统的同步机制。
原子变量在Java中主要通过java.util.concurrent.atomic包中的类实现,例如AtomicInteger、AtomicLong、AtomicBoolean等,这些类提供了各种原子操作方法,如incrementAndGet()、decrementAndGet()、compareAndSet()等,这些方法对底层变量执行原子操作,并返回操作后的值。
如下是使用AtomicInteger的代码示例:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicVariableExample {
public static void main(String[] args) {
AtomicInteger atomicInt = new AtomicInteger(0);
// 多个线程可以安全地并发更新这个原子整数
int oldValue = atomicInt.getAndIncrement(); // 原子性地自增并返回旧值
System.out.println(oldValue); // 输出 0
int newValue = atomicInt.get(); // 获取当前值
System.out.println(newValue); // 输出 1
}
}
AtomicInteger提供了一种原子性地更新整数值的方法,不需要使用synchronized关键字。
在这个示例中,increment()
方法原子地增加计数器的值,而getCount()
方法原子地获取计数器的值,由于这些操作是原子的,因此在多线程环境中使用AtomicInteger
比使用普通的int
变量更安全。原子变量在并发编程中非常有用,它们简化了线程间的同步,并提供了更高的性能。
Synchronizers(同步器)是Java并发编程中用于协调线程之间同步的组件,它们提供了一种机制,使线程能够等待、通知或限制其他线程的执行。主要包括:ReentrantLock、CountDownLatch、CyclicBarrier等,这些类为开发者提供了一系列高级同步工具,以便更好地控制线程之间的交互。
以下是几个常用的同步器:
同步器提供了一种灵活的机制来协调线程之间的同步,它们可以帮助开发者避免死锁、竞态条件和其他并发问题。
如下是CountDownLatch代码示例:
import java.util.concurrent.CountDownLatch;
public class SynchronizerExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 5;
CountDownLatch latch = new CountDownLatch(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {
new Thread(() -> {
System.out.println("Thread " + Thread.currentThread().getId() + " is ready");
latch.countDown(); // 通知 CountDownLatch 线程已准备就绪
}).start();
}
latch.await(); // 等待所有线程准备就绪
System.out.println("All threads are ready");
}
}
在上面代码汇总,使用 CountDownLatch
来同步多个线程,确保在所有线程都准备就绪之后再继续执行主线程。
END!