并发编程(2)-并发工具类

Fork-Join

核心思想

    分而治之:将一个大问题分割为相同的小问题,小问题之间无关联

    快速排序、归并排序、二分查询都属于分而治之的思想

Fork-Join的工作密取

    将一个大问题分割为相同的小问题交给多个线程执行,当一个线程先处理完任务之后,自动到其它线程的Task池中取出任务继续执行。

    ForkJoinPool 中维护着多个线程(一般为CPU 核数)在不断地执行Task,每个线程除了执行自己职务内的Task 之外,还会根据自己工作线程的闲置情况去获取其他繁忙的工作线程的Task,这样就能能够减少线程阻塞或是闲置的时间,提高CPU 利用率。

RecursiveTask:有返回值的任务

public class ForkJoinTest {

    // 数组大小
    private static final Integer ARR_LENGTH = 5000;
    // 每个子任务大小
    private static final Integer FORK_LENGTH = ARR_LENGTH / 100;
    private static int[] arr = new int[ARR_LENGTH];
    static{
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    // RecursiveTask 有返回值的任务
    private static class Sum extends RecursiveTask<Integer>{

        private int start, end;
        private int[] arrSum;

        // 构造
        public Sum(int[] arrSum, int start, int end) {
            this.arrSum = arrSum;
            this.start = start;
            this.end = end;
        }

        // 重写核心方法
        @Override
        protected Integer compute() {
            if (end - start < FORK_LENGTH) {
                int count = 0;
                for (int i = start; i <= end; i++) {
                    SleepTools.ms(1);
                    count += arr[i];
                }
                return count;
            }else{
                int mid = (start + end) / 2;
                Sum left = new Sum(arrSum, start, mid);
                Sum right = new Sum(arrSum, mid + 1, end);
                invokeAll(left, right);
                return left.join() + right.join();
            }
        }
    }

    public static void main(String[] args) {
        long start1 = System.currentTimeMillis();
        int result1 = 0;
        for (int i = 0; i < arr.length; i++) {
            SleepTools.ms(1);
            result1 += arr[i];
        }
        long end1 = System.currentTimeMillis();
        System.out.println("总和:" + result1 + "\t普通方式用时:" + (end1 - start1) + "毫秒");

        System.out.println("----------------------------------------------------------------");

        // ForkJoin线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        Sum sum = new Sum(arr, 0, arr.length-1);

        long start2 = System.currentTimeMillis();

        // invoke() 同步执行,带返回值,tasks会被同步到主线程
        // submit() 异步执行,带返回值,可通过task.get实现同步到主线程
        forkJoinPool.invoke(sum);
        Integer result2 = sum.join();

        long end2 = System.currentTimeMillis();
        System.out.println("总和:" + result2 + "\tForkJoin用时:" + (end2 - start2) + "毫秒");


    }
}

/*

总和:12497500	普通方式用时:8834毫秒
----------------------------------------------------------------
总和:12497500	ForkJoin用时:1148毫秒

*/

RecursiveAction:没有返回值的任务

// 没有返回值的任务
public class ForkJoinTest extends RecursiveAction {
    private File file;

    public ForkJoinTest(File file) {
        this.file = file;
    }

    @Override
    protected void compute() {

        List<FindDirsFiles> listDirs = new ArrayList<>();

        File[] files = file.listFiles();
        if (files!=null){
            for (File file : files) {
                if (file.isDirectory()) {
                    // 对每个子目录都新建一个子任务。
                    listDirs.add(new FindDirsFiles(file));
                } else {
                    // 遇到文件,检查。
                    if (file.getAbsolutePath().endsWith("txt")){
                        System.out.println("文件:" + file.getAbsolutePath());
                    }
                }
            }
            if (!listDirs.isEmpty()) {
                // 在当前的 ForkJoinPool 上调度所有的子任务。
                for (FindDirsFiles subTask : invokeAll(listDirs)) {
                    subTask.join();
                }
            }
        }
    }

    public static void main(String[] args) {
        // ForkJoin线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();

        ForkJoinTest forkJoinTest = new ForkJoinTest(new File("F:/"));

        // 异步提交,无返回值
        forkJoinPool.execute(forkJoinTest);

        SleepTools.ms(1);

    }
}

CountDownLatch

    CountDownLatch能够使一个线程等待其他线程完成各自的工作后再执行。

    CountDownLatch通过一个计数器来实现的,计数器初始值为初始任务的数量或少于初始任务的数量[因为每个任务可以多次修改计数器]。每当完成了一个任务后,计数器减1[CountDownLatch.countDown()]。当计算器值为0时,在闭锁上等待CountDownLatch.await()方法的线程就可以恢复执行任务。

    可以实现最大的并行性。

CountDownLatch使用示例

CyclicBarrier

    CyclicBarrier可以让一组线程达到一个同步点时被阻塞,直到这一组线程中的最后一个线程达到这个同步点时,阻塞才会自释放。

    CyclicBarrier有两个构造函数public CyclicBarrier(int parties)和public CyclicBarrier(int parties, Runnable barrierAction)。

    parties参数表示拦截的线程数量;barrierAction参数表示优先执行的线程。

CyclicBarrier使用示例

Semaphore

    Semaphore信号量是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理使用公共资源。

    Semaphore构造方法接受一个整形数字,表示可用的许可证数量。

    acquire():获取一个许可证。

    release():归还一个许可证。

    tryAcquire():尝试获取许可证。

    intavailablePermits():返回信号量中当前可用的许可证数。

    intgetQueueLength():返回正在等待获取许可证的线程数。

    booleanhasQueuedThread():是否有线程正在等待获取许可证。

    reducePermits(int reduction):减少reduction个许可证,是protected方法。

    getQueuedThread():返回所有等待获取许可证的线程集合,是protected方法。

public class SemaphoreTest {
    private static final int THREAD_NUM = 100;
    private static CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(THREAD_NUM + 1);

    // 初始5个信号
    private static final Semaphore init = new Semaphore(5), used = new Semaphore(0);

    // 初始5个资源
    private static LinkedList<Object> list = new LinkedList<Object>() {{
        add(new Object());
        add(new Object());
        add(new Object());
        add(new Object());
        add(new Object());
    }};

    public static void returnObject(Object object) throws Exception {
        if (null != object) {
            System.out.println("当前有" + init.getQueueLength() + "个线程在等待获取object -- 可用信号数:" + init.availablePermits());
            used.acquire();
            synchronized (list) {
                list.addLast(object);
            }
            init.release();
        }
    }

    public static Object getObject() throws Exception {
        Object o = null;
        init.acquire();
        synchronized (list) {
            o = list.removeFirst();
        }
        used.release();
        return o;
    }

    private static class ChdThread implements Runnable {
        @Override
        public void run() {
            try {
                countDownLatch.await();
                long start = System.currentTimeMillis();
                Object object = getObject();
                System.out.println(Thread.currentThread().getName() + "获取对象耗时:" + (System.currentTimeMillis() - start) + "毫秒");
                SleepTools.ms(new Random().nextInt(100) + 100);
                returnObject(object);
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("List初始大小:" + list.size());
        for (int i = 0; i < THREAD_NUM; i++) {
            Thread thread = new Thread(new ChdThread());
            thread.setName("第" + i + "个线程");
            thread.start();
            countDownLatch.countDown();
        }
        cyclicBarrier.await();
        System.out.println("List最终大小:" + list.size());
    }
}

Callable、Future、FutureTask

    Runnable接口的run()方法无返回值,所以线程执行之后无法返回结果。

    Callable接口的call()方法可以返回传递进来的泛型。

    Future接口是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。可以通过get()方法获取执行结果,get()方法会阻塞直到任务返回。

    FutureTask类是Future接口的实现

    FutureTask类实现了RunnableFuture接口;RunnableFuture接口继承了Runnable接口和Future接口。所以FutureTask即可以作为Runnable线程被执行,又可以作为Future得到Callable的返回值。

FutureTask使用示例

 

 

_____个人笔记_____((≡^⚲͜^≡))_____欢迎指正_____ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值