Java线程池使用场景和方法分析

简介: Java线程池使用场景和方法分析

线程池的研究

前期准备程序

Executor executor = ExecutorUtils.getExecutor(2);

创建新的线程,直接在最外层Executor

new Thread(new Runnable() {
   
   
    @Override
    public void run() {
   
   
        while (true){
   
   
            executor.execute(()->{
   
   
                logger.info(Thread.currentThread().getName());
            });
            try {
   
   
                Thread.sleep(1*1000);
            } catch (InterruptedException e) {
   
   
                e.printStackTrace();
            }
        }
    }
}).start();

效果是每秒钟线程池内的线程交替打印。

创建新的线程,在里面sleep

new Thread(new Runnable() {
   
   
    @Override
    public void run() {
   
   
        while (true){
   
   
            executor.execute(()->{
   
   
                logger.info(Thread.currentThread().getName());
                try {
   
   
                    Thread.sleep(1*1000);
                } catch (InterruptedException e) {
   
   
                    e.printStackTrace();
                }
            });
        }
    }
}).start();

代码直接出现异常。

直接在外部使用执行方法

executor.execute(()->{
   
   
    logger.info(Thread.currentThread().getName());
});

只打印了一次。

直接在外部循环打印

executor.execute(()->{
   
   
    while (true){
   
   
        logger.info(Thread.currentThread().getName());
        try {
   
   
            Thread.sleep(1*1000);
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        }
    }
});

只用到了一个线程。从这个实验我们可以得出一个结论,线程池的一堆线程是并发的时候使用的,如果没有形成并发,就不会使用新的线程。 所以我进行了接下来的尝试。

同时直接在外面调用两个外部循环打印

executor.execute(()->{
   
   
    while (true){
   
   
        logger.info(Thread.currentThread().getName());
        try {
   
   
            Thread.sleep(1*1000);
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        }
    }
});
executor.execute(()->{
   
   
    while (true){
   
   
        logger.info(Thread.currentThread().getName());
        try {
   
   
            Thread.sleep(1*1000);
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        }
    }
});

在这里插入图片描述

可以发现这就是并发的了。那么如果我想只在一个execute调用里面实现这样的功能该怎么做呢?

总结

Executor.execute仅仅只是执行代码,如果想要并发执行,只能进行多次调用。即,下面为完整的例子:

PriorityBlockingQueue<Integer> task = new PriorityBlockingQueue<>();
Executor executor = ExecutorUtils.getExecutor(2);
new Thread(new Runnable() {
   
   
    @Override
    public void run() {
   
   
        while (true){
   
   
            try {
   
   
                Thread.sleep(2*100);
            } catch (InterruptedException e) {
   
   
                e.printStackTrace();
            }
            task.add(new Double(Math.random()*10000).intValue());
        }
    }
}).start();
for (int i=0;i<2;i++){
   
   
    executor.execute(() -> {
   
   
        while (true) {
   
   
            Integer value = null;
            try {
   
   
                value = task.take();
            } catch (InterruptedException e) {
   
   
                e.printStackTrace();
            }
            if (Objects.nonNull(value)){
   
   
                logger.info(value);
                try {
   
   
                    Thread.sleep(MathUtils.getPositiveRandomInt(3)*1000);
                } catch (InterruptedException e) {
   
   
                    e.printStackTrace();
                }
            }
        }
    });
}

我用一个线程每隔200毫秒向队列里面丢一个数字,然后用线程池去取。然后executor.execute通过循环调用两次。就可以实现抢占式线程池调用了。

得想想上面的代码如何工具化。

批量执行任务集

ExecutorService service = ExecutorUtils.getExecutorService(2);
    List<Callable<Integer>> listTask = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
   
   
        listTask.add(() -> {
   
   
            int l = MathUtils.getPositiveRandomInt(100);
            int r = MathUtils.getPositiveRandomInt(100);
            Integer value = MathUtils.getRandomInt(Math.min(l, r), Math.max(l, r));
            logger.info(value);
            Thread.sleep(value);
            return value;
        });
    }
    try {
   
   
        List<Future<Integer>> futures = service.invokeAll(listTask);
        logger.info("------------------------------------");
        for (int i=0;i<100;i++){
   
   
            try {
   
   
                logger.info(futures.get(i).get());
            } catch (Exception e) {
   
   
                e.printStackTrace();
            }
        }
        service.shutdown();
    } catch (InterruptedException e) {
   
   
        e.printStackTrace();
    }
}

这种方式适合于批量执行任务,但是事先需要确定出总的任务量,等到执行完了再出结果。
通过

List<Future<Integer>> futures = service.invokeAll(listTask);

得到返回值,可以做一些合并操作。该方法适用于放在逐层局部需要进行并发执行的过程,例如爬虫,爬取一个网站的时候可以考虑逐层爬取,就可以使用该方法来做。

总结

当我们需要使用线程池的时候,如果不确定需要处理的对象有多少,就采用队列的方式,利用阻塞队列和抢占式的方法来实现线程池。如果对于每一次并发任务,知道

目录
相关文章
|
6天前
|
Java 关系型数据库 MySQL
Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
【4月更文挑战第12天】Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
33 3
|
1天前
|
存储 监控 Java
|
1天前
|
缓存 Java
Java并发编程:深入理解线程池
【4月更文挑战第26天】在Java中,线程池是一种重要的并发工具,它可以有效地管理和控制线程的执行。本文将深入探讨线程池的工作原理,以及如何使用Java的Executor框架来创建和管理线程池。我们将看到线程池如何提高性能,减少资源消耗,并提供更好的线程管理。
|
3天前
|
Java
Java 与垃圾回收有关的方法
Java 与垃圾回收有关的方法
|
3天前
|
存储 Java 测试技术
一文搞清楚Java中的方法、常量、变量、参数
在JVM的运转中,承载的是数据,而数据的一种变现形式就是“量”,量分为:**常量与变量**,我们在数学和物理学中已经接触过变量的概念了,在Java中的变量就是在程序运行过程中可以改变其值的量。
14 0
|
4天前
|
Java
Java中的并发编程:理解和应用线程池
【4月更文挑战第23天】在现代的Java应用程序中,性能和资源的有效利用已经成为了一个重要的考量因素。并发编程是提高应用程序性能的关键手段之一,而线程池则是实现高效并发的重要工具。本文将深入探讨Java中的线程池,包括其基本原理、优势、以及如何在实际开发中有效地使用线程池。我们将通过实例和代码片段,帮助读者理解线程池的概念,并学习如何在Java应用中合理地使用线程池。
|
8天前
|
存储 Java
Java动态转发代理IP的实现方法
Java动态转发代理IP的实现方法
23 11
|
8天前
使用代理IP池实现多线程的方法
使用代理IP池实现多线程的方法
|
9天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
14 0
Java接口中可以定义哪些方法?
|
10天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。

热门文章

最新文章