• JUC之Callable接口回顾和JUC辅助类


    Callable接口和JUC辅助类

    Callable接口:

    回顾:

    创建线程的四种方式:

    1. 继承Thread
    2. 实现runnable接口
    3. 实现callable接口
    4. 使用线程池

    之前的文章:多线程编程1-定义理解与三种实现方式

    Runnable和Callable接口的差异:

    1. Runnable无返回值,Callable有返回值
    2. Runnable不抛异常,Callable抛异常
    3. 实现名称不同,Runnable是run方法,Callable是call方法
    class MyThread1 implements Runnable{
    
        @Override
        public void run() {
            
        }
    }
    
    class MyThread2 implements Callable{
    
        @Override
        public Integer call() throws Exception {
            return 200;
        }
    }
    

    Runnable 接口实现类FutureTask

    FutureTask构造可以传递callable

    这是类的继承结构:

    别名:可取消的异步,简单的理解是当主线程中存在耗时高的任务时,可以单开一个子线程处理,主线程处理耗时少的任务,最终汇合在一起。

    需要注意的是,使用FutureTask当得到第一次结果后,第二次获取时直接返回结果,也可以说所有的任务只汇总一次。

    JUC辅助类:

    CountDownLatch(减少计数)

    定义:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier

    CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待

    /**
     * 问题,当六个人走出教室,则班长锁门
     */
    public class CountDownLatch07 {
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(6);
            for(int i = 1; i <= 6; i++){
                new Thread(() ->{
                    System.out.println(Thread.currentThread().getName()+" 号同学走出教室");
                    latch.countDown();
                },String.valueOf(i)).start();
            }
            latch.await();
            System.out.println(Thread.currentThread().getName()+"班长锁门");
        }
    }
    

    其使用的方法,CountDownLatch latch = new CountDownLatch(6)、latch.countDown()、latch.await();

    CyclicBarrier(循环栅栏)也可以实现CountDownLatch效果,CyclicBarrier在所有线程执行完毕之后是可以重用的。

    CyclicBarrier(循环栅栏)

    源码定义:

    一种同步辅助工具,它允许一组线程全部等待彼此到达公共屏障点。 CyclicBarriers 在涉及固定大小的线程组的程序中很有用,这些线程必须偶尔相互等待。 屏障被称为循环的,因为它可以在等待线程被释放后重新使用。

    简单的理解,当达到设置的要求后,执行特定的内容,相当于监听器;

    实例代码:

    public class JucUtils {
        private static final Integer NUMBER = 7;
        public static void main(String[] args) {
            //设置资源要求,达到后所需要执行的任务
            CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
                System.out.println("资源达到要求!");
            });
    		//对设置的目标前进
            for (int i = 1; i <= 7; i++) {
                int finalI = i;
                new Thread(()->{
                    try {
                        System.out.println("资源正在收集:"+ Thread.currentThread().getName());
                        //等待,当等待的线程数量到达设置的值,调用执行任务并释放这些线程。
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    

    感兴趣的可以看看源码:

    -------------------------CyclicBarrier--------------------
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties; 
        this.count = parties;  //设置临界值
        this.barrierCommand = barrierAction;
    }
    ----------------------------await-----------------------
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
    //------------------dowait部分代码-------------------
    final Generation g = generation;
    
    if (g.broken)
        throw new BrokenBarrierException();
    
    if (Thread.interrupted()) {
        breakBarrier();
        throw new InterruptedException();
    }
    
    int index = --count;  //每次等待,所需资源-1;
    if (index == 0) {  // tripped
        boolean ranAction = false;
        try {
            final Runnable command = barrierCommand;
            if (command != null)
                command.run();
            ranAction = true;
            nextGeneration();
            return 0;
        } finally {
            if (!ranAction)
                breakBarrier();
        }
    }
    
    //------nextGeneration----------
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();  //唤醒线程
        // set up next generation
        count = parties;
        generation = new Generation();
    }
    

    Semaphore(信号灯)

    如果对操作系统有所了解的话,该工具类就是信号量+pv操作的集合,对信号量的操作只有三种,初始化、p操作、v操作,其中信号量就是Semaphore初始化的(某种资源的数量),p操作对应的是semaphore.acquire(),信号量--,v操作对应的semaphore.release(),信号量++,当Semaphore初始化唯1时,则为互斥资源。

    package com.JUC;
    
    import java.util.Random;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 类比操作系统的中信号量PV操作
     */
    public class Semaphore07 {
        public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(3);
            for(int i = 1; i <= 6; i++){
                new Thread(()->{
                    try {
                        semaphore.acquire();  //加锁
                        System.out.println(Thread.currentThread().getName()+" 抢到了车位");
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));//随机时间停车
                        System.out.println(Thread.currentThread().getName()+"-------离开了车位");
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally{
                        semaphore.release(); //解锁
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    
  • 相关阅读:
    Vue 静态资源文件下载 以pdf为例
    高德地图POI搜索
    矩阵转置(二维列表行列互换)
    列表动态填加元素
    关于判断值为None的处理
    第2章 小程序项目结构配置
    Python 基础编码风格
    Visual Studio — 模块计算机类型“x64”与目标计算机类型“x86”冲突
    Visual Studio — 无法解析的外部符号的几种可能
    Visual Studio — 包含目录、附加包含目录、库目录、附加库目录、附加依赖项之详解
  • 原文地址:https://www.cnblogs.com/xbhog/p/15776889.html
Copyright © 2020-2023  润新知