博客概述
在这篇博客里面主要进行并发包的一些工具类的讲解,主要是作用的讲解以及代码演示。
Concurrent.util常用类
CyclicBarric
假设有这样的一个场景,每个线程代表一个跑步运动员,当所有运动员都准备好以后才出发,只要有一个人没有准备好,大家都等待。
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UseCyclicBarrier {
static class Runner implements Runnable {
private CyclicBarrier barrier;
private String name;
public Runner(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(1000 * (new Random()).nextInt(5));
System.out.println(name + " 准备OK.");
barrier.await(); //已经待命,等其他人待命之后,可以继续往下执行
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name + " Go!!");
}
}
public static void main(String[] args) throws IOException, InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(3); // 3 ,需要3个待命信号,有3个线程使用,需要三次await
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new Thread(new Runner(barrier, "zhangsan")));
executor.submit(new Thread(new Runner(barrier, "lisi")));
executor.submit(new Thread(new Runner(barrier, "wangwu")));
executor.shutdown();
}
}
CountDownLatch
他经常用于监听某些初始化的操作,等初始化操作执行完毕后,通知主线程继续工作。这个通常被一个线程等待其他线程就绪的场景会用到。
import java.util.concurrent.CountDownLatch;
public class UseCountDownLatch {
public static void main(String[] args) {
//需要2个就绪信号,才能继续执行。
final CountDownLatch countDown = new CountDownLatch(2);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("进入线程t1" + "等待其他线程处理完成...");
//需要继续执行的线程,但是在等待某种资源的准备完毕,方可继续执行,就用await方法
countDown.await();
System.out.println("t1线程继续执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t2线程进行初始化操作...");
Thread.sleep(3000);
System.out.println("t2线程初始化完毕,通知t1线程继续...");
//资源x准备完毕
countDown.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t3线程进行初始化操作...");
Thread.sleep(4000);
System.out.println("t3线程初始化完毕,通知t1线程继续...");
//资源y准备完毕
countDown.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t3.start();
}
}
主线程等待某种资源加载完毕,使用信号量的等待函数。
然后在子线程中使用发出信 号的countdown函数,通知资源已经准备就绪,可以继续往下运行。
CyclicBarric与CountDownLatch区别
-
countdownlatch
一个线程等待,其他的发信号让 等待的执行。
针对一个线程等待,别的线程(1或N个)去激活它。 -
cyclicbarrier
是阻塞的线程都参与执行。针对的是多个线程。因为存在多个线程准备的时间长短不同,使之一起运行。
Callable和Future使用
这个其实就是我们之前的Future模式的api实现,jdk给我们实现了一个封装,使用非常简单。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
//接口泛型为返回值,接口里面的方法为异步执行的方法
public class UseFuture implements Callable<String>{
private String para;
public UseFuture(String para){
this.para = para;
}
/**
* 这里是真实的业务逻辑,其执行可能很慢
*/
@Override
public String call() throws Exception {
//模拟执行耗时
Thread.sleep(5000);
String result = this.para + "处理完成";
return result;
}
//主控制函数
public static void main(String[] args) throws Exception {
String queryStr = "query";
//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类
FutureTask<String> future = new FutureTask<String>(new UseFuture(queryStr));
FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr));
//创建一个固定线程的线程池且线程数为1,
ExecutorService executor = Executors.newFixedThreadPool(2);
//这里提交任务future,则开启线程执行RealData的call()方法执行
//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
Future f1 = executor.submit(future); //单独启动一个线程去执行的,调用就立即执行。
Future f2 = executor.submit(future2);
System.out.println("请求完毕");
try {
//这里可以做额外的数据操作,也就是主程序执行其他业务逻辑
System.out.println("处理实际的业务逻辑...");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待。get是同步的,但是里面的内容是异步的
System.out.println("数据:" + future.get());
System.out.println("数据:" + future2.get());
executor.shutdown();
}
}