侧边栏壁纸
博主头像
赫兹

谁辜负过自己,说不上可惜

  • 累计撰写 18 篇文章
  • 累计创建 13 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

ConcurrentHashMap+多线程 实现实时进度条

赫兹
2022-09-25 / 0 评论 / 0 点赞 / 433 阅读 / 2,660 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-09-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

需求背景

​ 📌最近遇到需要定时统计的任务,由于任务比较耗时,产品希望能在前端显示任务的实时统计进度。

使用说明

🥝创建用户类User.java用户模拟定时任务批量处理用户信息

/**
 * @author 赫兹
 */
@Data
@AllArgsConstructor
public class User {
    
    private Long id;

    private String name;

    private String nickName;

    private String sex;

    private String email;
    
    private int age;
}

🥝编写多线程工具类ThreadPoolUtil.java,用于维护、管理线程池

/**
 * @author 赫兹
 */
public class ThreadPoolUtil {

    private static ThreadPoolExecutor threadPool;

    private ThreadPoolUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * 无返回值,直接执行任务
     */
    public static void execute(Runnable runnable) {
        getThreadPool().execute(runnable);
    }

    /**
     * 有返回值直,提交任务
     */
    public static <T> Future<T> submit(Callable<T> callable) {
        return getThreadPool().submit(callable);
    }


    /**
     * 获取线程池
     *
     * @return 线程池对象
     */
    public static ThreadPoolExecutor getThreadPool() {
        if (threadPool != null) {
            return threadPool;
        } else {
            synchronized (ThreadPoolUtil.class) {
                if (threadPool == null) {
                    threadPool = new ThreadPoolExecutor(4, 8, 60L, TimeUnit.SECONDS,
                            new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.CallerRunsPolicy());
                }
                return threadPool;
            }
        }
    }

}

🥝编写进度设置、进度查询工具类ProgressBarUtil.java

/**
 * @author 赫兹
 */
public class ProgressBarUtil {

    private ProgressBarUtil() {
        throw new IllegalStateException("Utility class");
    }

    private static final ConcurrentHashMap<Long, Double> MAP = new ConcurrentHashMap<>();

    /**
     * 设置任务进度
     *
     * @param taskId   任务id
     * @param progress 进度
     */
    public static void setProcessBar(Long taskId, Double progress) {
        int maximum = 100;
        if (taskId == null || progress == null) {
            throw new ApplicationException(ApplicationExceptionEnum.PARAM_NULL);
        }
        if (progress < 0 || progress > maximum) {
            throw new ApplicationException(ApplicationExceptionEnum.PARAM_ERROR);
        }
        MAP.put(taskId, progress);
    }

    /**
     * 根据任务id查询任务进度
     *
     * @param taskIds 待查询的任务id集合
     * @return 任务id和进度值的映射
     */
    public static Map<Long, Double> getTaskProcessMap(Collection<Long> taskIds) {
        Map<Long, Double> map = new ConcurrentHashMap<>(4);
        for (Long taskId : taskIds) {
            Optional.ofNullable(MAP.get(taskId)).ifPresent(v -> map.put(taskId, v));
        }
        return map;
    }

}

🥝编写单元测试,测试结果

/**
 * @author 赫兹
 */
@Slf4j
public class JUnitTest {

    @Test
    public void testProgressBarUtil() throws InterruptedException {
        //任务id
        Long taskId = 1008666L;
        //创建用户数据(模拟数据库查询操作),用于模拟批量处理用户信息任务,即任务task1需要处理3条用户数据
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 203; i++) {
            User user = new User((long) i, "张三_"+i, "小张_"+i, "男", "1234567@qq.com", 23);
            userList.add(user);
        }

        //模拟前端轮询每隔1秒查询任务进度接口
        ThreadPoolUtil.execute(()->{
            List<Long> taskIds= new ArrayList<>(Arrays.asList(taskId));
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Map<Long, Double> taskProcessMap = ProgressBarUtil.getTaskProcessMap(taskIds);
                log.info("任务进度Map集合{}",taskProcessMap);
            }
        });
        Thread.sleep(1000);

        //开始处理用户信息,并设置任务进度
        //统计任务集合
        List<Future<String>> futureList = new ArrayList<>();
        userList.forEach(user -> {
            //将任务提交线程池管理
            Future<String> threadResult = ThreadPoolUtil.submit(() -> {
                //模拟处理用户的信息的业务耗时
                Thread.sleep(1000);
                return user.getName();
            });
            futureList.add(threadResult);
        });
        //设置每次任务计算完成后的进度
        final BigDecimal[] progress = {BigDecimal.ZERO};
        //任务进度条最大值
        int maximum = 100;
        //计算每个任务占总进度的占比
        BigDecimal taskProgress = BigDecimal.valueOf(maximum).divide(BigDecimal.valueOf(userList.size()), 2, RoundingMode.HALF_DOWN);
        //等待线程执行结果,所有线程执行完毕后,再继续
        futureList.forEach(threadResult -> {
            try {
                //每次任务完成后,就加上进度
                threadResult.get();
                progress[0] = progress[0].add(taskProgress);
                ProgressBarUtil.setProcessBar(taskId, progress[0].doubleValue());
            } catch (InterruptedException | ExecutionException e) {
                log.error(e.getMessage(), e);
                Thread.currentThread().interrupt();
            }
        });
        //任务执行完成后,将进度条设置100%(计算进度占比时,采用的去尾法取两位小数,因此任务统计完成后,进度可能会不足100%)
        ProgressBarUtil.setProcessBar(taskId, 100.0);

        //不让主线程这么快退出,为了让前端查询的子线程有更好模拟效果
        Thread.sleep(60000);
    }

}

🥝结果输出

15:58:30.807 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{}
15:58:31.817 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{}
15:58:32.829 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=1.47}
15:58:33.837 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=2.94}
15:58:34.840 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=4.41}
15:58:35.841 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=5.88}
15:58:36.852 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=7.35}
15:58:37.865 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=8.82}
15:58:38.874 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=10.29}
15:58:39.888 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=11.76}
15:58:40.901 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=13.23}
15:58:41.912 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=14.7}
15:58:42.922 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=16.17}
15:58:43.934 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=17.64}
15:58:44.937 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=19.11}
15:58:45.943 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=20.58}
15:58:46.947 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=22.05}
15:58:47.955 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=23.52}
15:58:48.965 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=25.48}
15:58:49.976 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=27.44}
15:58:50.992 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=27.93}
15:58:52.003 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=29.4}
15:58:53.009 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=30.87}
15:58:54.056 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=32.34}
15:58:55.063 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=33.81}
15:58:56.076 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=35.28}
15:58:57.085 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=36.75}
15:58:58.096 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=38.22}
15:58:59.109 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=39.69}
15:59:00.118 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=41.16}
15:59:01.123 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=42.63}
15:59:02.134 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=44.1}
15:59:03.147 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=47.04}
15:59:04.162 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=48.02}
15:59:05.174 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=48.51}
15:59:06.187 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=49.98}
15:59:07.189 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=51.45}
15:59:08.202 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=52.92}
15:59:09.214 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=54.39}
15:59:10.218 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=55.86}
15:59:11.232 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=57.33}
15:59:12.240 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=58.8}
15:59:13.250 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=60.27}
15:59:14.252 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=61.74}
15:59:15.260 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=63.21}
15:59:16.270 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=64.68}
15:59:17.276 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=66.15}
15:59:18.286 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=67.62}
15:59:19.297 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=69.09}
15:59:20.310 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=70.56}
15:59:21.323 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=73.5}
15:59:22.336 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=73.5}
15:59:23.349 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=74.97}
15:59:24.353 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=76.44}
15:59:25.363 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=78.4}
15:59:26.368 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=79.38}
15:59:27.378 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=80.85}
15:59:28.386 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=82.32}
15:59:29.396 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=83.79}
15:59:30.407 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=85.26}
15:59:31.412 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=86.73}
15:59:32.424 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=88.2}
15:59:33.439 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=89.67}
15:59:34.448 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=91.14}
15:59:35.458 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=92.61}
15:59:36.473 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=94.08}
15:59:37.483 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=95.55}
15:59:38.487 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=97.02}
15:59:39.495 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=98.49}
15:59:40.502 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=100.0}
15:59:41.512 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=100.0}
15:59:42.520 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=100.0}
15:59:43.526 [pool-1-thread-1] INFO com.hz.codecase.test.JUnitTest - 任务进度Map集合{1008666=100.0}

Process finished with exit code -1

0

评论区