Fork/Join框架
将一个大任务,进行拆分(fork)成n个小任务(直到不可再分),再将小任务运算结果进行join汇总。
工作密取:例如任务分成了两个线程执行,当A线程执行完后B线程还未执行完,则A线程会去B线程偷取任务执行,执行完之后再放入B,被窃取任务线程永远从头部拿任务执行,而窃取任务的线程从尾部拿任务执行。
RecursiveAction,用于没有返回结果的任务
RecursiveTask,用于有返回值的任务
fork/join框架应该慎用,因为会造成线程之间的上下文切换,如果任务非常简单,那么不会起到省时作用。
代码演示:
/**
* 同步方法演示,需要返回值
*/
static class MyTask extends RecursiveTask<Integer> {
private final int HOLD = 10; // 任务判断阈值
private int[] src; // 实际统计的数组
private int startIndex; //开始统计的下标
private int endIndex;
public MyTask(int[] src, int startIndex, int endIndex) {
this.src = src;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
protected Integer compute() {
if (endIndex - startIndex < HOLD) {
int count = 0;
for (int i = startIndex; i <= endIndex; i++) {
count += src[i];
}
return count;
} else {
// 那么继续拆分
int mid = (startIndex + endIndex) / 2;
MyTask left = new MyTask(src, startIndex, mid);
MyTask right = new MyTask(src, mid + 1, endIndex);
invokeAll(left, right);
return left.join() + right.join();
}
}
}
/**
* 将大数组拆分为小数组求和
* @param args
*/
public static void main(String[] args) {
int[] src = new int[4000];
for (int i = 0; i < 4000; i++) {
src[i] = i;
}
ForkJoinPool joinPool = new ForkJoinPool();
MyTask myTask = new MyTask(src, 0, src.length -1);
joinPool.invoke(myTask);
}
/**
* 查询某文件夹下的文件
* 异步方法演示,无返回值
*/
static class MyTask2 extends RecursiveAction {
private File path;
public MyTask2(File path) {
this.path = path;
}
@Override
protected void compute() {
List<MyTask2> subTasks = new ArrayList<>();
File[] files = path.listFiles();
if (files != null) {
for (File file: files) {
if (file.isDirectory()) {
subTasks.add(new MyTask2(file));
} else {
if (file.getAbsolutePath().endsWith(".txt")) {
System.out.println("txt文件:" + file.getAbsolutePath());
}
}
}
}
if (!subTasks.isEmpty()) {
// invokeAll 得到提交的每个任务的结果
for (MyTask2 myTask : invokeAll(subTasks)) {
myTask.join();
}
}
}
}
public static void main(String[] args) {
ForkJoinPool joinPool = new ForkJoinPool();
MyTask2 myTask = new MyTask2(new File("d:/"));
joinPool.execute(myTask);
myTask.join();
}
CountDownLatch
等待n个线程完成后,再执行主线程
public class CountDownTest {
// 3 表示需要等待的子线程数量
static CountDownLatch countDownLatch = new CountDownLatch(2);
static class OneThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
}
static class TwoThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown(); // 计数器减一
}
}
public static void main(String[] args) throws InterruptedException {
OneThread one = new OneThread();
TwoThread two = new TwoThread();
LocalDateTime start = LocalDateTime.now();
new Thread(one).start();
new Thread(two).start();
// 等待子线程
countDownLatch.await();
LocalDateTime end = LocalDateTime.now();
Duration duration = Duration.between(start, end);
System.out.println(duration.toMillis());
}
}
CyclicBarrier
是让一组线程达到某个屏障,一直阻塞,直到所有线程都到达屏障后,屏障开放,线程开始运行。
// parties 屏障开放之前,必须等待的线程数 ; barrierAction 屏障开放之后,要执行的线程
CyclicBarrier(int parties, Runnable barrierAction)
public static void main(String[] args) throws ExecutionException, InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
executorService.shutdown();
}
CyclicBarrier和CountDownLatch 区别
- countdownlatch放行条件>=线程数,CyclicBarrier放行条件=线程数
- CountDownLatch是不可重置的,所以无法重用;而CyclicBarrier则没有这种限制,可以重用
- 它两目的就不同
Semaphore
控制同时访问资源的线程数量,可以用在流量控制
/**
* @Auther: ZXL
* @Date: 2019/4/5
* @Description: 模拟简单的连接池
*/
public class SemaphoreDemo {
private static final int POOL_SIZE = 10;
private static LinkedList<MyConnection> pool = new LinkedList<>();
// useFul可用的连接池 useLess不可用的连接池
private Semaphore useFul, useLess;
public SemaphoreDemo() {
this.useFul = new Semaphore(POOL_SIZE);
this.useLess = new Semaphore(0);
}
static {
for (int i = 0; i < POOL_SIZE; i++) {
pool.add(new MyConnection());
}
}
/**
* 归还链接
*/
public void returnCon(MyConnection connection) throws InterruptedException {
if (connection != null) {
System.out.println("等待连接池的线程" + useFul.getQueueLength()+
"可用连接池的数量" + useFul.availablePermits());
useLess.acquire();
synchronized (pool) {
pool.addLast(connection);
}
useFul.release();
}
}
/**
* 取出连接
*/
public MyConnection takeCon() throws InterruptedException {
// 如果拿不到就会阻塞在这
useFul.acquire();
MyConnection connection;
synchronized (pool) {
connection = pool.removeFirst();
}
// 已用连接+1
useLess.release();
return connection;
}
public static void main(String[] args) {
SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
for (int i = 0; i < 30; i++) {
Thread a = new Thread(() -> {
Random random = new Random();
try {
MyConnection connection = semaphoreDemo.takeCon();
Thread.sleep(1000 + random.nextInt(100));
semaphoreDemo.returnCon(connection);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
a.start();
}
}
}
Callable、Future和FutureTask
![](https://i-blog.csdnimg.cn/blog_migrate/d06efcdac3e3e04a925ae3a0ee1e1c65.png)
Callable接口是有返回值的一种线程启动方式。
Future:
可以用在异步调用服务,例如dubbo的异步调用。
isDone(),结束,正常还是异常结束,或者自己取消,返回true;
isCancelled() 任务完成前被取消,返回true;
cancel(boolean):
- 任务还没开始,返回false
- 任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
- 任务已经结束,返回false
public class FutureDemo { static class CallDemo implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("计算开始"); int num = 0; for (int i = 0; i < 10000; i++) { num++; } System.out.println("计算结束:" + num); return num; } } public static void main(String[] args) throws ExecutionException, InterruptedException { CallDemo callDemo = new CallDemo(); FutureTask<Integer> futureTask = new FutureTask<>(callDemo); new Thread(futureTask).start(); //System.out.println("中断结果:" + futureTask.cancel(true)); System.out.println("正常获取的结果:" + futureTask.get()); System.out.println("中断结果:" + futureTask.cancel(true)); } }