Java并发工具类详解
在Java编程中,处理并发任务时,我们常常需要使用一些工具来确保线程之间的同步和数据交换。本文将详细介绍几个重要的并发工具类,包括
CountDownLatch
、
CyclicBarrier
、
Exchanger
和
Phaser
。
1. CountDownLatch
CountDownLatch
是一个强大且易于使用的同步对象,适用于一个线程必须等待一个或多个事件发生的场景。
下面是一个简单的示例,展示了
CountDownLatch
的基本用法:
import java.util.concurrent.CountDownLatch;
class MyThread implements Runnable {
private CountDownLatch latch;
MyThread(CountDownLatch cdl) {
this.latch = cdl;
new Thread(this).start();
}
public void run() {
for (int i = 0; i < 5; i++) {
latch.countDown();
}
}
}
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(5);
new MyThread(cdl);
cdl.await();
System.out.println("Main thread resumes");
}
}
在上述代码中,
main
方法中创建了一个初始计数为5的
CountDownLatch
对象
cdl
。然后创建了一个
MyThread
实例,该线程在
run
方法中循环5次,每次调用
countDown()
方法将
cdl
的计数减1。
main
线程调用
await()
方法,会暂停执行,直到
cdl
的计数减为0。
2. CyclicBarrier
在并发编程中,经常会遇到一组两个或多个线程必须在预定的执行点等待,直到该组中的所有线程都到达该点的情况。
CyclicBarrier
类可以帮助我们处理这种情况。
CyclicBarrier
有两个构造函数:
-
CyclicBarrier(int numThreads)
-
CyclicBarrier(int numThreads, Runnable action)
其中,
numThreads
指定了在继续执行之前必须到达屏障的线程数量。在第二个构造函数中,
action
指定了一个线程,当所有线程到达屏障时将执行该线程。
以下是使用
CyclicBarrier
的一般步骤:
1. 创建一个
CyclicBarrier
对象,指定要等待的线程数量。
2. 当每个线程到达屏障时,调用该对象的
await()
方法。这将暂停线程的执行,直到所有其他线程也调用
await()
方法。
3. 当指定数量的线程到达屏障时,
await()
方法将返回,线程继续执行。如果指定了
action
,则执行该线程。
await()
方法有两种形式:
-
int await()
throws
InterruptedException
,
BrokenBarrierException
-
int await(long wait, TimeUnit tu)
throws
InterruptedException
,
BrokenBarrierException
,
TimeoutException
下面是一个示例代码,展示了
CyclicBarrier
的使用:
import java.util.concurrent.*;
class BarDemo {
public static void main(String args[]) {
CyclicBarrier cb = new CyclicBarrier(3, new BarAction());
System.out.println("Starting");
new MyThread(cb, "A");
new MyThread(cb, "B");
new MyThread(cb, "C");
}
}
class MyThread implements Runnable {
CyclicBarrier cbar;
String name;
MyThread(CyclicBarrier c, String n) {
cbar = c;
name = n;
new Thread(this).start();
}
public void run() {
System.out.println(name);
try {
cbar.await();
} catch (BrokenBarrierException exc) {
System.out.println(exc);
} catch (InterruptedException exc) {
System.out.println(exc);
}
}
}
class BarAction implements Runnable {
public void run() {
System.out.println("Barrier Reached!");
}
}
上述代码中,创建了一个
CyclicBarrier
对象
cb
,指定需要等待3个线程到达屏障。当3个线程都到达屏障时,执行
BarAction
线程。
CyclicBarrier
可以重复使用,每次指定数量的线程调用
await()
方法时,它都会释放等待的线程。
3. Exchanger
Exchanger
是一个非常有趣的同步类,它旨在简化两个线程之间的数据交换。其操作非常简单:它会一直等待,直到两个不同的线程调用其
exchange()
方法。当这种情况发生时,它会交换两个线程提供的数据。
Exchanger
是一个泛型类,声明如下:
Exchanger<V>
其中,
V
指定了要交换的数据类型。
Exchanger
只有一个方法
exchange()
,有两种形式:
-
V exchange(V objRef)
throws
InterruptedException
-
V exchange(V objRef, long wait, TimeUnit tu)
throws
InterruptedException
,
TimeoutException
下面是一个示例代码,展示了
Exchanger
的使用:
import java.util.concurrent.Exchanger;
class ExgrDemo {
public static void main(String args[]) {
Exchanger<String> exgr = new Exchanger<String>();
new UseString(exgr);
new MakeString(exgr);
}
}
class MakeString implements Runnable {
Exchanger<String> ex;
String str;
MakeString(Exchanger<String> c) {
ex = c;
str = new String();
new Thread(this).start();
}
public void run() {
char ch = 'A';
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
str += ch++;
}
try {
str = ex.exchange(str);
} catch (InterruptedException exc) {
System.out.println(exc);
}
}
}
}
class UseString implements Runnable {
Exchanger<String> ex;
String str;
UseString(Exchanger<String> c) {
ex = c;
new Thread(this).start();
}
public void run() {
for (int i = 0; i < 3; i++) {
try {
str = ex.exchange(new String());
System.out.println("Got: " + str);
} catch (InterruptedException exc) {
System.out.println(exc);
}
}
}
}
在上述代码中,创建了两个线程:
MakeString
线程创建一个空字符串,并将其填充数据;
UseString
线程创建一个空字符串,与
MakeString
线程交换数据。
4. Phaser
Phaser
是另一个同步类,其主要目的是实现一个或多个活动阶段的线程同步。它有点像
CyclicBarrier
,但支持多个阶段。
Phaser
定义了四个构造函数,这里介绍两个常用的:
-
Phaser()
-
Phaser(int numParties)
第一个构造函数创建一个注册计数为0的
Phaser
对象,第二个构造函数将注册计数设置为
numParties
。
以下是使用
Phaser
的一般步骤:
1. 创建一个新的
Phaser
实例。
2. 通过调用
register()
方法或在构造函数中指定参与方的数量,向
Phaser
注册一个或多个参与方。
3. 对于每个注册的参与方,让
Phaser
等待所有注册的参与方完成一个阶段。参与方可以通过调用
Phaser
提供的方法(如
arrive()
或
arriveAndAwaitAdvance()
)来发出信号。
4. 当所有参与方都到达后,阶段完成,
Phaser
可以进入下一个阶段(如果有)或终止。
Phaser
提供了一些常用方法:
-
int register()
:注册一个参与方,并返回注册的阶段号。
-
int arrive()
:表示一个参与方完成了某个任务,返回当前阶段号。
-
int arriveAndAwaitAdvance()
:表示一个参与方完成了一个阶段,并等待其他参与方完成该阶段,返回下一个阶段号。
-
int arriveAndDeregister()
:表示一个参与方完成了一个阶段并注销,返回当前阶段号。
-
final int getPhase()
:获取当前阶段号。
下面是一个示例代码,展示了
Phaser
的使用:
import java.util.concurrent.*;
class PhaserDemo {
public static void main(String args[]) {
Phaser phsr = new Phaser(1);
int curPhase;
System.out.println("Starting");
new MyThread(phsr, "A");
new MyThread(phsr, "B");
new MyThread(phsr, "C");
// Wait for all threads to complete phase one
curPhase = phsr.getPhase();
phsr.arriveAndAwaitAdvance();
System.out.println("Phase " + curPhase + " Complete");
// Wait for all threads to complete phase two
curPhase = phsr.getPhase();
phsr.arriveAndAwaitAdvance();
System.out.println("Phase " + curPhase + " Complete");
curPhase = phsr.getPhase();
phsr.arriveAndAwaitAdvance();
System.out.println("Phase " + curPhase + " Complete");
// Deregister the main thread
phsr.arriveAndDeregister();
if (phsr.isTerminated()) {
System.out.println("The Phaser is terminated");
}
}
}
class MyThread implements Runnable {
Phaser phsr;
String name;
MyThread(Phaser p, String n) {
phsr = p;
name = n;
phsr.register();
new Thread(this).start();
}
public void run() {
System.out.println("Thread " + name + " Beginning Phase One");
phsr.arriveAndAwaitAdvance();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println(e);
}
System.out.println("Thread " + name + " Beginning Phase Two");
phsr.arriveAndAwaitAdvance();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println(e);
}
System.out.println("Thread " + name + " Beginning Phase Three");
phsr.arriveAndDeregister();
}
}
在上述代码中,
main
方法创建了一个初始参与方计数为1的
Phaser
对象
phsr
。然后创建了三个
MyThread
实例,每个线程有三个阶段。
main
线程和每个
MyThread
线程通过调用
arriveAndAwaitAdvance()
方法来同步各个阶段的执行。
总结
本文介绍了Java中的几个重要并发工具类,包括
CountDownLatch
、
CyclicBarrier
、
Exchanger
和
Phaser
。这些工具类可以帮助我们更好地处理并发任务,确保线程之间的同步和数据交换。在实际应用中,我们可以根据具体需求选择合适的工具类。
流程图
graph TD;
A[开始] --> B[创建CountDownLatch];
B --> C[创建MyThread];
C --> D[MyThread调用countDown];
D --> E{CountDownLatch计数是否为0};
E -- 是 --> F[Main线程恢复执行];
E -- 否 --> D;
F --> G[创建CyclicBarrier];
G --> H[创建多个MyThread];
H --> I[MyThread调用await];
I --> J{所有线程是否到达屏障};
J -- 是 --> K[执行指定线程];
J -- 否 --> I;
K --> L[创建Exchanger];
L --> M[创建MakeString和UseString线程];
M --> N[线程调用exchange];
N --> O{两个线程是否都调用exchange};
O -- 是 --> P[交换数据];
O -- 否 --> N;
P --> Q[创建Phaser];
Q --> R[注册参与方];
R --> S[参与方执行阶段任务];
S --> T[参与方调用arrive或arriveAndAwaitAdvance];
T --> U{所有参与方是否完成阶段};
U -- 是 --> V[进入下一阶段或终止];
U -- 否 --> T;
表格
| 工具类 | 功能 | 构造函数 | 常用方法 |
|---|---|---|---|
| CountDownLatch | 一个线程等待一个或多个事件发生 |
CountDownLatch(int count)
|
await()
,
countDown()
|
| CyclicBarrier | 一组线程在预定点等待所有线程到达 |
CyclicBarrier(int numThreads)
,
CyclicBarrier(int numThreads, Runnable action)
|
await()
|
| Exchanger | 两个线程交换数据 |
Exchanger<V>()
|
exchange(V objRef)
|
| Phaser | 实现一个或多个活动阶段的线程同步 |
Phaser()
,
Phaser(int numParties)
|
register()
,
arrive()
,
arriveAndAwaitAdvance()
,
arriveAndDeregister()
,
getPhase()
|
Java并发工具类详解(续)
5. 深入理解Phaser的高级用法
前面我们介绍了
Phaser
的基本用法和常用方法,接下来深入探讨它的一些高级特性。
5.1 重写
onAdvance()
方法
Phaser
允许我们重写
onAdvance()
方法,以此来控制当一个阶段完成并进入下一个阶段时的具体行为。
onAdvance()
方法的签名如下:
protected boolean onAdvance(int phase, int regParties)
其中,
phase
表示当前阶段的编号,
regParties
表示当前注册的参与方数量。当
onAdvance()
方法返回
true
时,
Phaser
将终止;返回
false
时,
Phaser
将继续进入下一个阶段。
下面是一个示例,展示了如何重写
onAdvance()
方法来控制
Phaser
只执行指定数量的阶段:
import java.util.concurrent.*;
// 扩展Phaser以允许只执行特定数量的阶段
class MyPhaser extends Phaser {
int numPhases;
MyPhaser(int parties, int phaseCount) {
super(parties);
numPhases = phaseCount - 1;
}
// 重写onAdvance()以执行指定数量的阶段
protected boolean onAdvance(int p, int regParties) {
// 此println()语句仅用于说明,通常onAdvance()不会显示输出
System.out.println("Phase " + p + " completed.\n");
// 如果所有阶段都已完成,返回true
if (p == numPhases || regParties == 0) return true;
// 否则,返回false
return false;
}
}
class PhaserDemo2 {
public static void main(String args[]) {
MyPhaser phsr = new MyPhaser(1, 4);
System.out.println("Starting\n");
new MyThread(phsr, "A");
new MyThread(phsr, "B");
new MyThread(phsr, "C");
// 等待指定数量的阶段完成
while (!phsr.isTerminated()) {
phsr.arriveAndAwaitAdvance();
}
System.out.println("The Phaser is terminated");
}
}
// 使用Phaser的执行线程
class MyThread implements Runnable {
Phaser phsr;
String name;
MyThread(Phaser p, String n) {
phsr = p;
name = n;
phsr.register();
new Thread(this).start();
}
public void run() {
while (!phsr.isTerminated()) {
System.out.println("Thread " + name + " Beginning Phase " + phsr.getPhase());
phsr.arriveAndAwaitAdvance();
// 稍作暂停以防止输出混乱,仅用于说明,不是Phaser正常运行所必需的
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
}
在上述代码中,
MyPhaser
类继承自
Phaser
,并重写了
onAdvance()
方法。在
main
方法中,创建了一个
MyPhaser
对象,指定要执行4个阶段。当所有阶段完成后,
onAdvance()
方法返回
true
,
Phaser
终止。
5.2
Phaser
的其他方法
除了前面介绍的常用方法,
Phaser
还提供了一些其他有用的方法:
-
int awaitAdvance(int phase)
:等待指定阶段的转换。如果传入的阶段编号不等于当前阶段编号,或者
Phaser
已终止,则该方法立即返回。
-
int awaitAdvanceInterruptibly(int phase)
:可中断的等待指定阶段的转换。
-
void bulkRegister(int parties)
:一次性注册多个参与方。
-
int getRegisteredParties()
:获取当前注册的参与方数量。
-
int getArrivedParties()
:获取已经到达当前阶段的参与方数量。
-
int getUnarrivedParties()
:获取尚未到达当前阶段的参与方数量。
-
void forceTermination()
:强制
Phaser
进入终止状态。
6. 并发工具类的应用场景总结
不同的并发工具类适用于不同的应用场景,下面对它们的应用场景进行总结:
-
CountDownLatch
:适用于一个线程需要等待其他多个线程完成某些任务后才能继续执行的场景。例如,主线程等待多个子线程完成数据加载后再进行后续处理。
-
CyclicBarrier
:适用于一组线程需要在某个特定点同步的场景。例如,多个线程并行计算,在计算完成后需要同时进行结果汇总。
-
Exchanger
:适用于两个线程之间需要交换数据的场景。例如,一个线程负责生产数据,另一个线程负责消费数据,通过
Exchanger
可以方便地进行数据交换。
-
Phaser
:适用于需要同步多个阶段的场景。例如,一个复杂的任务可以分为多个阶段,每个阶段需要多个线程协同完成,使用
Phaser
可以很好地控制每个阶段的同步。
流程图
graph TD;
A[创建MyPhaser] --> B[注册参与方];
B --> C[参与方执行阶段任务];
C --> D[参与方调用arriveAndAwaitAdvance];
D --> E{所有参与方是否完成阶段};
E -- 是 --> F{是否到达指定阶段};
F -- 是 --> G[终止Phaser];
F -- 否 --> H[进入下一阶段];
H --> C;
E -- 否 --> D;
表格
| 工具类 | 应用场景 | 优点 | 缺点 |
|---|---|---|---|
| CountDownLatch | 一个线程等待多个线程完成任务 | 简单易用,实现线程等待逻辑 | 只能使用一次,不能重置 |
| CyclicBarrier | 一组线程在特定点同步 | 可以重复使用,支持在屏障点执行额外操作 | 线程数量固定,修改较麻烦 |
| Exchanger | 两个线程交换数据 | 简化数据交换逻辑 | 只适用于两个线程之间的交换 |
| Phaser | 同步多个阶段 | 支持多个阶段的同步,可灵活控制阶段转换 | 相对复杂,学习成本较高 |
总结
本文详细介绍了Java中的几个重要并发工具类,包括
CountDownLatch
、
CyclicBarrier
、
Exchanger
和
Phaser
。通过对这些工具类的学习,我们可以更好地处理并发任务,确保线程之间的同步和数据交换。在实际应用中,我们需要根据具体的需求选择合适的工具类,并合理使用它们的方法和特性。同时,我们还深入探讨了
Phaser
的高级用法,包括重写
onAdvance()
方法和使用其他辅助方法,这些高级特性可以帮助我们更灵活地控制线程的同步。希望本文能对大家在并发编程方面有所帮助。
超级会员免费看
1021

被折叠的 条评论
为什么被折叠?



