1. Fork-Join
什么是分而治之?
规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解。分而治之思想应用:在快速排序、归并排序、动态规划等等。
工作密取(workStealing):线程A完成了工作之后可以被调度去帮线程B完成task,为了避免竞争获取任务,从相反的方向获取任务。
Fork/Join使用的标准范式:
使用:
1.创建RecursiveTask、RecursiveAction的实现类对象myTask
2.ForkJoinPool pool = new ForkJoinPool();
3.调用
pool.invoke(myTask); // 同步调用
pool.execute(myTask); // 异步异步
继承RecursiveTask类,重写compute方法
带返回结果值
案例:统计整形数组中所有元素的和
继承RecursiveAction类,重写compute方法
无返回值
案例:遍历文件夹寻找指定类型文件
public class FindDirsFiles extends RecursiveAction{
private File path;//当前任务需要搜寻的目录
public FindDirsFiles(File path) {
this.path = path;
}
@Override
protected void compute() {
//任务列表
List<FindDirsFiles> subTasks = new ArrayList<>();
File[] files = path.listFiles();
if(files!=null) {
for(File file:files) {
//不是文件则放进任务列表里
if(file.isDirectory()) {
subTasks.add(new FindDirsFiles(file));
}else {
//遇到文件,检查
if(file.getAbsolutePath().endsWith("txt")) {
System.out.println("文件:"+file.getAbsolutePath());
}
}
}
if(!subTasks.isEmpty()) {
//把任务列表丢进去同步执行
for(FindDirsFiles subTask:invokeAll(subTasks)) {
subTask.join();//等待子任务执行完成
}
}
}
}
public static void main(String [] args){
try {
// 用一个 ForkJoinPool 实例调度总任务
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("D:/"));
pool.execute(task);//异步调用
System.out.println("Task is Running......");
Thread.sleep(1);
int otherWork = 0;
for(int i=0;i<100;i++){
otherWork = otherWork+i;
}
System.out.println("Main Thread done sth......,otherWork="+otherWork);
task.join();//阻塞的方法
System.out.println("Task end");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. CountDownLatch
作用:是一组线程等待其他的线程完成工作以后在执行,加强版join
await()
用来等待,countDown()
负责计数器的减一
案例实现:主线程 和 业务线程 等待 初始化线程 完成后继续自己的工作
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化线程(后面主线程会new四个实例出来)
private static class InitThread implements Runnable{
@Override
public void run() {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work......");
latch.countDown();//初始化线程完成工作了,countDown方法只扣减一次;
for(int i =0;i<2;i++) {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ........continue do its work");
}
}
}
//业务线程(等待准备完毕后执行)
private static class BusiThread implements Runnable{
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i =0;i<3;i++) {
System.out.println("BusiThread_"+Thread.currentThread().getId()
+" do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
//单独的初始化线程,初始化分为2步,需要扣减两次
new Thread(new Runnable() {
@Override
public void run() {
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 1st......");
latch.countDown();//每完成一步初始化工作,扣减一次
System.out.println("begin step 2nd.......");
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 2nd......");
latch.countDown();//每完成一步初始化工作,扣减一次
}
}).start();
new Thread(new BusiThread()).start();
for(int i=0;i<=3;i++){
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("Main do ites work........");
}
}
SleepTools 代码:
public class SleepTools {
/**
* 按秒休眠
* @param seconds 秒数
*/
public static final void second(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
/**
* 按毫秒数休眠
* @param seconds 毫秒数
*/
public static final void ms(int seconds) {
try {
TimeUnit.MILLISECONDS.sleep(seconds);
} catch (InterruptedException e) {
}
}
}
3.CyclicBarrier
CyclicBarrier(int parties)
让一组线程达到某个屏障,被阻塞,一直到组内最后一个线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行。
CyclicBarrier(int parties, Runnable barrierAction)
,屏障开放,barrierAction定义的任务会执行。
//创建
CyclicBarrier barrier = new CyclicBarrier(5);
//等待
barrier.await();
/**
* 线程执行到await()方法处会阻塞,当有五个线程都执行到await(),
* 它们会一起继续执行后面的代码。
**/
CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制,CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件>=线程数,CyclicBarrier放行条件=线程数
举个简单的栗子:
CountDownLatch:小明和小红和小华一起出去自驾游,
//等两个人-小明、小红 latch.await() //等着 )
CountDownLatch latch = new CountDownLatch(2) ;
小华启动了车子,等着大家搬好东西上车,小明上车后说我准备好了,
latch.countDown();
小红上车后说我准备好了,
latch.countDown();
(latch值为0了)就可以出发咯。
CyclicBarrier:小明和小红和小华一起去旅游,相约在火车站见面,,先到的等着,三人到齐一起出发。
4. Semaphore
控制同时访问某个特定资源的线程数量,常用于流量控制
Semaphore sm = new Semaphore(10) ;//大小为10
sm.acquire();//拿一个 -1
sm.release();//释放一个 +1
4. Exchange
两个线程间的数据交换, 因为只能适用于两个线程间的交换,使用较少。
示例:
public class UseExchange {
private static final Exchanger<Set<String>> exchange
= new Exchanger<Set<String>>();
public static void main(String[] args) {
//第一个线程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setA = new HashSet<String>();//存放数据的容器
try {
//添加数据
setA.add("A");
setA = exchange.exchange(setA);//交换set
//处理交换后的数据
for (String s : setA){
System.out.println("我是线程A,我的值为:"+ s);
}
} catch (InterruptedException e) {
}
}
}).start();
//第二个线程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setB = new HashSet<String>();//存放数据的容器
try {
//添加数据
setB.add("B");
setB = exchange.exchange(setB);//交换set
//处理交换后的数据
for (String s : setB){
System.out.println("我是线程B,我的值为:"+ s);
}
} catch (InterruptedException e) {
}
}
}).start();
}
}
5. Callable、Future和FutureTask
isDone()
,结束,正常还是异常结束,或者自己取消,返回true;
isCancelled()
任务完成前被取消,返回true;
cancel(boolean)
:
1、 任务还没开始,返回false
2、 任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
3、 任务已经结束,返回false
包含图片和文字的文档的处理:图片(云上),可以用future去取图片,主线程继续解析文字。
案例:使用子线程计算求和后返回给主线程结果,并测试中断方法:
public class UseFuture {
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算");
Thread.sleep(2000);//这个睡眠会响应中断,抛出异常让线程停止,否则没有处理中断,线程不会管别人是否让他中断
for(int i=0;i<5000;i++) {
sum = sum+i;
}
System.out.println("Callable子线程计算完成,结果="+sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();
FutureTask<Integer> futureTask = new FutureTask<Integer>(useCallable);
new Thread(futureTask).start();
Random r = new Random();
SleepTools.second(1);
if(r.nextBoolean()) {//随机决定是获得结果还是终止任务
System.out.println("Get UseCallable result = "+futureTask.get());
}else {
System.out.println("中断计算");
futureTask.cancel(true);
}
}
}