CountDownLatch实现线程间通信
经典笔试题:
CountDownLatch:减为0,没钱了,一起跑路!
1、自定义容器,提供新增元素(add)和获取元素数量(size)方法。
2、启动两个线程。线程1向容器中新增10个数据。线程2监听容器元素数量,当容器元素数量为5时,线程2输出信息并终止。
package Threads;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
public class CountLatch {
static final MyContainer myContainer = new MyContainer();
static final CountDownLatch countLatch = new CountDownLatch(1);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println("add to MyContainer " + i);
myContainer.add(i);
if(myContainer.size() == 5){
countLatch.countDown();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (myContainer.size() != 5){
try {
countLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("2结束!");
}
}).start();
}
}
class MyContainer{
private ArrayList<Integer> list;
public MyContainer(){
list = new ArrayList<>();
}
public void add(int n){
list.add(n);
}
public int size(){
return list.size();
}
}
CyclicBarrier
通俗点:就是我遇到障碍(通过的大门),等多个线程都到了障碍(大门),大家一起进去。
应用场景:用于多线程计算的情况。
多线程计算还有fork/join框架
ForkJoinTask,和Forkoinpoll。
原理:ForkTask的fork,调用forkWorkThread 的pushTask()方法,把当前任务放入到ForkJoinTask数组队列中,然后继续调用forkoinPoll的singalWork()方法,创建线程干活。
join,就是阻塞,得到结果,注意调用doJoin()方法,如果任务状态完成,就返回结果;取消的话,就抛出取消异常
比如计算 1+2+3+4
//1.继承 2.实现compute 3.提交
package Threads;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ForkTasks extends @zhangkun426<Integer> {
/**
* 1.main中使用ForkJoinPool 对象。返回future对象
* 2.compute方法中,fork 干活。join 结果
*/
private static final int THRESLOD = 2;
private int start;
private int end;
public ForkTasks(int start, int end) {
this.start = start;
this.end = end;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkTasks forkTasks = new ForkTasks(1,4);
if(forkTasks.isCompletedAbnormally()){
System.out.println(forkTasks.getException());
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> result = forkJoinPool.submit(forkTasks);
System.out.println(result.get());
}
@Override
protected Integer compute() {
int sum = 0;
boolean can = (end - start) < THRESLOD;
if(can){
for (int i = start; i <= end; i++) {
sum += i;
}
}else{
int mid = (start + end)/2;
ForkTasks left = new ForkTasks(start, mid);
ForkTasks right = new ForkTasks(mid + 1, end);
left.fork();
right.fork();
int leftResult = left.join();
int rightResult = right.join();
sum = leftResult + rightResult;
}
return sum;
}
}
比如计算 4个线程计算数据
package Threads;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
public class CyclicBarriers implements Runnable{
private CyclicBarrier cyclicBarrier = new CyclicBarrier(4,this);
private Executor executor = Executors.newFixedThreadPool(4);
private ConcurrentHashMap<String,Integer> concurrentHashMap = new ConcurrentHashMap<>();
public static void main(String[] args) {
CyclicBarriers cyclicBarriers = new CyclicBarriers();
cyclicBarriers.count();
}
public void count(){
for (int i = 0; i < 4; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
concurrentHashMap.put(Thread.currentThread().getName(),1);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void run() {
int result = 0;
Set<Map.Entry<String, Integer>> entries = concurrentHashMap.entrySet();
for(Map.Entry<String,Integer> entry : entries){
result += entry.getValue();
}
System.out.println(result);
}
}
使用四个线程池,分别计算每个数据,然后由CyclicBarriers 线程汇总4个线程的结果,结果为4。
区别
- countDownLatch 计数器只能用一次,而CyclicBarrier可以调用reset()重置。
- CyclicBarrier提供其他方法,比如了解线程是否中断。
Semaphore
信号量:控制访问资源的线程数量。
限流器使用。
比如数据库连接30个线程要连接,控制10个线程可以并发执行。
package Threads;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
private static final int THREAD = 30;
private static Executor executor = Executors.newFixedThreadPool(THREAD);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for (int i = 0; i < THREAD; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println(Thread.currentThread().getName() + "获取");
Thread.sleep(3000);
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}