JAVA线程并发工具类笔记整理Fork-Join、CountDownLatch、CyclicBarrier、Semaphore、Exchange、Callable、FutureTask

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);
		}
		
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值