本文主要介绍的工具包括:
-
CountDownLatch
-
Semaphore
-
CyclicBarrier
-
Exchanger
CountDownLatch
CountDownLatch可以使一个或多个线程等待一组事件发生。在CountDownLatch内部维护一个计数器(被初始化为一个正整数),表示需要等待事件的数量。countDown()
方法减少一个事件数量,await()
将等待直到计数器为零的时候,才继续执行await后面的代码。如果计数器不为零,那么await将一直会阻塞等待直到计数器为零,或者阻赛线程中断/超时。
@Test
public void test() throws InterruptedException {
// thread number
final int threadNum = Runtime.getRuntime().availableProcessors() + 1;
// start event
final CountDownLatch startEvent = new CountDownLatch(1);
// finish event
final CountDownLatch finishEvent = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// await for start
startEvent.await();
System.out.println(Thread.currentThread() + " start at : " + System.currentTimeMillis());
// current thread finish
finishEvent.countDown();
} catch (InterruptedException ignore) {
}
}
}).start();
// sleep 0.5ms
TimeUnit.MILLISECONDS.sleep(500);
}
long startTime = System.currentTimeMillis();
startEvent.countDown();
// wait for all thread finish
finishEvent.await();
System.out.println("total finish cost : " + (System.currentTimeMillis() - startTime) + "ms");
}
这个例子展示了如何在同一时间启动threadNum
个线程,并且这threadNum
个线程都完成后,记录执行结果。startEvent.await()
将等待直到调用startEvent.countDown()
。这是,所有线程在同一时间启动。当每个线程执行完毕的时候,会调用finishEvent.countDown()
通知给主线程,finishEvent.await()
将等待直到所有子线程都执行完毕。
打印结果:
Thread[Thread-1,5,main] start at : 1359782125125
Thread[Thread-8,5,main] start at : 1359782125125
Thread[Thread-7,5,main] start at : 1359782125125
Thread[Thread-6,5,main] start at : 1359782125125
Thread[Thread-5,5,main] start at : 1359782125125
Thread[Thread-3,5,main] start at : 1359782125125
Thread[Thread-0,5,main] start at : 1359782125125
Thread[Thread-2,5,main] start at : 1359782125125
Thread[Thread-4,5,main] start at : 1359782125125
total finish cost : 1ms
Semaphore
Semaphore在内部持有一个虚拟的许可组(初始化的时候可以设置虚拟组的数量),当执行某个操作的时候,调用acquire
获得许可,在操作执行完成后调用release
释放许可。如果没有许可可用,那么acquire
方法会一直阻赛直到有许可可用为止,或者执行获取许可的线程终端或阻赛。
Semaphore可以用来控制某种资源的使用数量,或者同时使用特定资源的数量。利用这个特性,可以实现某种资源的资源池或者对容器实加边界。
@Test
public void test() throws InterruptedException {
final BoundedList<Integer> list = new BoundedList<>(5);
new Thread(new Runnable() {
@Override
public void run() {
int index = 0;
while (true) {
try {
list.add(index++);
System.out.println(System.currentTimeMillis() + " add " + index);
} catch (InterruptedException ignore) {
}
}
}
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(new Runnable() {
@Override
public void run() {
int index = 0;
while (true) {
try {
list.remove(index++);
System.out.println(System.currentTimeMillis() + " remove " + index);
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException ignore) {
}
}
}
}).start();
Thread.currentThread().join();
}
static class BoundedList<T> {
private final List<T> list;
private final Semaphore semaphore;
public BoundedList(int bound) {
this.list = new ArrayList<>();
semaphore = new Semaphore(bound);
}
public boolean add(T o) throws InterruptedException {
semaphore.acquire();
boolean added = false;
try {
added = list.add(o);
return added;
} finally {
if (!added) {
semaphore.release();
}
}
}
public boolean remove(T o) {
boolean removed = list.remove(o);
if (removed) {
semaphore.release();
}
return removed;
}
}
这个例子展示了一个带边界的List
,当向集合中添加元素的时候,首先获取许可。如果添加失败了,那么释放许可
。当删除集合中的元素的时候,如果删除成功,释放一个许可。这样就能保证集合中的元素都是获得许可后才添加进来的,从而保证了集合的边界。
打印结果:
1359787233784 add 1
1359787233784 add 2
1359787233784 add 3
1359787233784 add 4
1359787233784 add 5
1359787234787 add 6
1359787234787 remove 1
1359787235288 remove 2
1359787235288 add 7
1359787235789 remove 3
1359787235789 add 8
1359787236290 remove 4
1359787236290 add 9
….
在这个例子中,生产者向集合中添加元素,消费者删除元素,因为生产者的速度大于消费者,所以当集合中元素等于5的时候,就必须等待消费者删除一个元素后才能再继续添加,从打印结果可以看出这点。
CyclicBarrier
CyclicBarrier和CountDownLatch有些类似,它阻塞一组线程直到某个事件发生。可以把CyclicBarrier理解成一个障碍,当所有线程都到达这个"障碍"的时候,才能继续下个事件。如果所有线程到达barrier处,barrier打开释放所有线程,并且barrier可以继续使用。如果await
方法超时,或者被中断,那么认为barrier被打破,所有在await上阻塞的线程都将抛出BrokenBarrierException
。
@Test
public void test() throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() + 1);
final int gate_threshold = 4;
final CyclicBarrier gate = new CyclicBarrier(gate_threshold, new Runnable() {
@Override
public void run() {
System.out.println("4 threads arrived, gate open...");
}
});
while (true) {
TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000));
exec.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + " arrived");
try {
gate.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
}
这个例子假设有一堆线程到达gate
处,每当到达gate
处的线程数达到gate_threshold
时,gate
打开释放这些线程并进入下次一次循环。
Exchanger
Exchanger提供两个线程以线程安全的形式交换数据,exchange等待另一个线程到达exchange方法,然后把数据给另一个线程并且接收另一个线程交换过来的数据。
@Test
public void test() throws InterruptedException {
final int size =10;
final Exchanger<List<Integer>> exchanger = new Exchanger<>();
final List<Integer> emptyList = new ArrayList<>();
final List<Integer> fullList = new ArrayList<>();
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
List<Integer> currentList = emptyList;
while (true){
if(currentList.size()<size){
int num = (int)( Math.random() * 10000);
currentList.add(num);
System.out.println("producer : " + num);
try {
TimeUnit.MILLISECONDS.sleep((long) (Math.random()*1000));
} catch (InterruptedException ignore) {
}
}else {
try {
System.out.println("producer : list full wait exchange");
currentList = exchanger.exchange(currentList);
System.out.println("producer : exchanged");
} catch (InterruptedException ignore) {
}
}
}
}
});
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
List<Integer> currentList = fullList;
while (true){
if(currentList.size()!=0){
Integer remove = currentList.remove(0);
System.out.println("consumer : " + remove);
try {
TimeUnit.MILLISECONDS.sleep((long) (Math.random()*1000));
} catch (InterruptedException ignore) {
}
}else {
try {
System.out.println("consumer : list empty wait exchange");
currentList = exchanger.exchange(currentList);
System.out.println("consumer : exchanged");
} catch (InterruptedException ignore) {
}
}
}
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
这个例子展示了一个生产者/消费者的例子。当生产者填慢list后,等待交换。同样当消费者消耗完list,也等待交换。
相关推荐
Java并发工具包是Java平台中的一个关键特性,它位于`java.util.concurrent`包下,为开发者提供了高效、安全的多线程编程支持。这个工具包的设计目标是简化并发编程,提高程序的性能和可维护性,同时避免了线程同步的...
5. **并发处理:** `java.util.concurrent` 包含了大量的并发工具类,如`ExecutorService` 和 `Future` 用于管理线程池和异步任务,`CountDownLatch` 和 `CyclicBarrier` 用于协调多个线程的同步。 6. **数学计算:...
5. **并发工具类**:`java.util.concurrent`包提供了高级并发工具,如ExecutorService、Semaphore、CountDownLatch、CyclicBarrier等,帮助开发者更有效地管理多线程程序。 6. **字符串处理**:`String`类是Java中...
4. **`java.util.concurrent`包**:这个包包含了线程安全的数据结构(如ConcurrentHashMap)和并发工具类,如ExecutorService、Semaphore、CyclicBarrier等,用于高效地处理多线程场景。 5. **`java.util.logging....
1. **线程与进程**:书中首先会介绍操作系统中的线程和进程概念,以及它们在Java中的实现。Java通过`Thread`类支持线程的创建和管理,而`Runnable`接口提供了另一种实现多线程的方式。 2. **同步机制**:Java中的...
7. **并发处理**: `java.util.concurrent`包提供了丰富的并发工具,如ExecutorService、Semaphore、CyclicBarrier、CountDownLatch等,帮助开发者高效地编写多线程程序。 8. **字符串处理**: `String`类是不可变的...
6. **其他有用类**:`java.util.concurrent`包提供了线程安全的集合和并发工具,如`ExecutorService`,`Semaphore`等,有助于多线程编程。`java.util.Random`用于生成随机数,`java.util.UUID`则生成全局唯一的...
### Java 并发核心编程知识点...通过上述技术细节和技术要点的介绍,我们可以看出Java提供了丰富的并发工具和支持,让开发者能够构建高效、可靠的多线程应用程序。理解和掌握这些概念和技术是成功进行并发编程的关键。
Java平台提供了丰富的并发工具和API,如线程、锁、同步机制、并发集合以及执行器服务等,这些工具和API都在本书中有详尽的介绍。 1. **线程基础**:Java中的线程是并发执行的基本单位。文档会讲解如何创建和管理...
"java常用工具类整理"这个主题涵盖了Java开发中常用的工具类,这些类在日常编程中非常实用,能大大提高开发效率。这里我们将深入探讨一些常见的Java工具类,并结合`com-jarvis-base`这个文件名,推测这是一个基础...
在Java编程领域,工具类(Util Classes)是程序员日常工作中不可或缺的部分。它们提供了一系列静态方法,简化了常见的任务,如字符串处理、日期时间操作、集合操作等。"java常用工具类集合"是一个综合性的资源,它...
8. **`java.util.concurrent`** 包:提供并发工具类,如`ExecutorService`、`Future`和`Semaphore`,帮助管理线程和控制并发。 9. **`java.util.Map.Entry`**:表示Map中的键值对,常用于遍历Map。 10. **`java....
4. **Cache缓存**: 在Java中,我们可以使用`java.util.concurrent.ConcurrentHashMap`作为基础实现缓存,因为它提供了线程安全的并发操作。另外,Google的Guava库提供了更高级的`LoadingCache`,它支持自动加载和...
Java中的线程池是由`java.util.concurrent`包中的`ExecutorService`接口及其实现类如`ThreadPoolExecutor`提供的。线程池可以有效地管理和控制并发执行的任务数量,避免频繁创建和销毁线程带来的性能开销。通过设置...
14. **Thread**: 代表Java中的线程,用于实现并发操作。 15. **ThreadLocal**: 提供线程局部变量,每个线程都有自己的副本,互不影响。 16. **UUID**: 生成全局唯一标识符(UUID),常用于数据库主键生成。 17. *...
本篇文章将详细解析Java中常用的工具类,包括字符处理、文件处理、时间操作以及图片处理等方面的知识点。 1. **字符处理**: - `java.lang.String`:String类是Java中最基础也是最常用的数据类型之一,提供了大量...