前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java多线程(线程池)

java多线程(线程池)

原创
作者头像
一百减一是零
发布2024-08-06 09:35:30
1860
发布2024-08-06 09:35:30
举报
文章被收录于专栏:线程

java线程池

Java线程池是一种预先创建一定数量的线程,并将任务提交给这些线程执行的机制。线程池可以避免频繁创建和销毁线程,提高程序的性能和响应速度。

为什么要创建线程池:

降低资源消耗:通过重复利用已创建的线程,避免线程的频繁创建和销毁,降低资源消耗。

提高响应速度:当任务到达时,任务不需要等待线程创建就能立即执行。

提高线程的可管理性:线程池可以进行统一的分配、调优和监控,有利于提高线程的可管理性。

线程池应用场景:

快速响应用户请求(IO密集型任务):如用户要查看商品信息,需要将商品维度的一系列信息聚合起来展示给用户。通过使用线程池,可以快速响应用户请求。

快速处理批量任务(CPU密集型任务):如统计某个报表,需要计算出全国各个门店中有哪些商品有某种属性,用于后续营销策略的分析。通过使用线程池,可以快速处理批量任务。

如何创建线程池:

Java中的线程池可以通过以下几种方式创建:

Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。方法只有一个参数,即线程池的大小。

Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。

Executors.newSingleThreadExecutor:创建一个单个线程数的线程池,它可以保证先进先出的执行顺序。

Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。

ThreadPoolExecutor:通过ThreadPoolExecutor类创建线程池,可以自定义线程池的参数,如核心线程数、最大线程数、队列类型等。

有什么区别:

使用 Executors 工厂类中的 newCachedThreadPool() 和 newFixedThreadPool() 方法:

优点:

- 使用起来比较简单,不需要自己手动管理线程池的状态;

- 可以快速创建线程池。

缺点:

- 无法自定义线程池的大小和饱和策略;

- 不够灵活。

使用 Executors 工厂类中的 newSingleThreadExecutor() 方法:

优点:

- 创建单个线程的线程池;

- 适用于需要顺序执行任务的场景。

缺点:

- 无法自定义线程池的大小和饱和策略;

- 不够灵活。

使用 ThreadPoolExecutor 类来创建线程池:

参数:

- corePoolSize:核心线程数

- maximumPoolSize:最大线程数

- keepAliveTime:线程存活时间

- unit:时间单位

- workQueue:任务队列

- threadFactory:线程工厂

- handler:饱和策略

优点:

- 可以根据实际情况来自定义线程池的大小、任务队列等;

- 可以灵活地设置饱和策略。

缺点:

- 对于不熟悉线程池的开发人员来说,使用起来比较麻烦;

- 需要手动管理线程池的状态。

核心线程数:指线程池中保持活动的最小线程数。如果线程池中的当前线程数小于核心线程数,则会在需要时创建新的线程。

最大线程数:指线程池中允许的最大线程数。如果队列满了,并且当前线程数已经达到最大线程数,则会在需要时创建新的线程。

队列类型:指用于存储待执行任务的队列类型。常见的队列类型有直接提交队列、有界队列、无界队列等。

拒绝策略:指当任务队列已满,且所有线程都在工作,但仍然无法处理新任务时的处理策略。常见的拒绝策略有抛出异常、拒绝任务、阻塞任务等。

以上几种方式分别对应不同的应用场景和需求。其中,Executors类提供了一种简单的方式来创建线程池,但它的参数相对固定,不适合进行复杂的配置。而ThreadPoolExecutor类则提供了更多的参数选项,可以灵活地配置线程池的各种属性,适用于更为复杂的场景。

不同线程池对应的应用场景

Executors.newFixedThreadPool:适用于需要控制并发线程数量的场景,例如,当需要确保每个请求都能得到响应时,或者当请求量很大时,可以控制并发线程数量,避免系统过载。-------需要创建固定数量的线程来执行长时间运行的任务;

Executors.newCachedThreadPool:适用于执行大量短期异步任务,例如,Web请求处理或短期的后台任务。这种线程池能快速创建和销毁线程,以应对大量突发任务。大量短时间的任务,如网络请求

Executors.newSingleThreadExecutor:适用于需要保证任务按照提交顺序执行的场景,例如,当需要保证任务的串行执行时。需要顺序执行任务的场景,如一个线程代表一个用户请求

Executors.newScheduledThreadPool:适用于需要定时或周期性执行任务的场景,例如,定时任务、定时备份等。

ThreadPoolExecutor:适用于需要自定义线程池参数的场景,例如,需要根据任务的特性(如优先级、执行时间等)来调整线程池的配置。这种线程池提供了更多的灵活性和控制性。复杂的应用场景,需要根据实际情况来动态地调整线程池的大小和饱和策略,如一个 Web 服务器上服务对多个用户的请求时

案例

代码语言:java
复制
 
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
 
/**
 * 线程处理类
 */
public class ExecutorProcessPool {
 
    private ExecutorService executor;
    private static ExecutorProcessPool pool = new ExecutorProcessPool();
    private final int threadMax = 10;
 
    private ExecutorProcessPool() {
        System.out.println("threadMax>>>>>>>" + threadMax);
        executor = ExecutorServiceFactory.getInstance().createFixedThreadPool(threadMax);
    }
 
    public static ExecutorProcessPool getInstance() {
        return pool;
    }
 
    /**
     * 关闭线程池,这里要说明的是:调用关闭线程池方法后,线程池会执行完队列中的所有任务才退出
     */
    public void shutdown() {
        executor.shutdown();
    }
 
    /**
     * 提交任务到线程池,可以接收线程返回值
     *
     * @param task
     * @return
     */
    public Future<?> submit(Runnable task) {
        return executor.submit(task);
    }
 
    /**
     * 提交任务到线程池,可以接收线程返回值
     *
     * @param task
     * @return
     */
    public Future<?> submit(Callable<?> task) {
        return executor.submit(task);
    }
 
    /**
     * 直接提交任务到线程池,无返回值
     *
     * @param task
     */
    public void execute(Runnable task) {
        executor.execute(task);
    }
 
}
代码语言:java
复制
 
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
 
 
/**
 * 线程池构造工厂
 */
public class ExecutorServiceFactory {
    private static ExecutorServiceFactory executorFactory = new ExecutorServiceFactory();
    /**
     * 定时任务线程池
     */
    private ExecutorService executors;
 
    private ExecutorServiceFactory() {
    }
 
    /**
     * 获取ExecutorServiceFactory
     *
     * @return
     */
    public static ExecutorServiceFactory getInstance() {
        return executorFactory;
    }
 
    /**
     * 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
     *
     * @return
     */
    public ExecutorService createScheduledThreadPool() {
        // CPU个数
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        // 创建
        executors = Executors.newScheduledThreadPool(availableProcessors * 10, getThreadFactory());
        return executors;
    }
 
    /**
     * 创建一个使用单个 worker 线程的
     * Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,
     * 那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的
     * newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
     *
     * @return
     */
    public ExecutorService createSingleThreadExecutor() {
        // 创建
        executors = Executors.newSingleThreadExecutor(getThreadFactory());
        return executors;
    }
 
    /**
     * 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用
     * execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60
     * 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor
     * 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。
     *
     * @return
     */
    public ExecutorService createCachedThreadPool() {
        // 创建
        executors = Executors.newCachedThreadPool(getThreadFactory());
        return executors;
    }
 
    /**
     * 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads
     * 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务
     * ,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止
     * ,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
     *
     * @return
     */
    public ExecutorService createFixedThreadPool(int count) {
        // 创建
        executors = Executors.newFixedThreadPool(count, getThreadFactory());
        return executors;
    }
 
 
    /**
     * 获取线程池工厂
     *
     * @return
     */
    private ThreadFactory getThreadFactory() {
        return new ThreadFactory() {
            AtomicInteger sn = new AtomicInteger();
 
            public Thread newThread(Runnable r) {
                SecurityManager s = System.getSecurityManager();
                ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
                Thread t = new Thread(group, r);
                t.setName("任务线程 - " + sn.incrementAndGet());
                return t;
            }
        };
    }
}
代码语言:java
复制
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
 
/**
 * 测试类
 */
public class ExecutorTest {
 
    public static void main(String[] args) {
 
        ExecutorProcessPool pool = ExecutorProcessPool.getInstance();
 
       /* for (int i = 0; i < 200; i++) {
            Future<?> future = pool.submit(new ExcuteTask1(i+""));
//          try {
//              如果接收线程返回值,future.get() 会阻塞,如果这样写就是一个线程一个线程执行。所以非特殊情况不建议使用接收返回值的。
//              System.out.println(future.get());
//          } catch (Exception e) {
//              e.printStackTrace();
//          }
        }*/
        List<String> list = new ArrayList(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "n", "m", "o", "p"));
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            pool.execute(new ExcuteTask2(s + ""));
        }
 
        //关闭线程池,如果是需要长期运行的线程池,不用调用该方法。
        //监听程序退出的时候最好执行一下。
        List<String> list2 = new ArrayList(Arrays.asList("的", "飞", "个", "个", "3"));
        for (int i = 0; i < list2.size(); i++) {
            String s = list2.get(i);
            pool.execute(new ExcuteTask2(s + ""));
        }
//        pool.shutdown();
    }
 
    /**
     * 执行任务1,实现Callable方式
     */
    static class ExcuteTask1 implements Callable<String> {
        private String taskName;
 
        public ExcuteTask1(String taskName) {
            this.taskName = taskName;
        }
 
        @Override
        public String call() throws Exception {
            try {
//              Java 6/7最佳的休眠方法为TimeUnit.MILLISECONDS.sleep(100);
//              最好不要用 Thread.sleep(100);
                TimeUnit.MILLISECONDS.sleep((int) (Math.random() * 1000));// 1000毫秒以内的随机数,模拟业务逻辑处理
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("-------------这里执行业务逻辑,Callable TaskName = " + taskName + "-------------");
            return ">>>>>>>>>>>>>线程返回值,Callable TaskName = " + taskName + "<<<<<<<<<<<<<<";
        }
    }
 
    /**
     * 执行任务2,实现Runable方式
     */
    static class ExcuteTask2 implements Runnable {
        private String taskName;
 
        public ExcuteTask2(String taskName) {
            this.taskName = taskName;
        }
 
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep((int) (Math.random() * 1000));// 1000毫秒以内的随机数,模拟业务逻辑处理
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-------------这里执行业务逻辑,Runnable TaskName = " + taskName + "-------------");
        }
 
    }
}

输出结果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java线程池
  • 为什么要创建线程池:
  • 线程池应用场景:
  • 如何创建线程池:
    • 有什么区别:
      • 优点:
        • 缺点:
        • 使用 Executors 工厂类中的 newSingleThreadExecutor() 方法:
          • 优点:
            • 缺点:
            • 使用 ThreadPoolExecutor 类来创建线程池:
              • 参数:
                • 优点:
                  • 缺点:
                  • 不同线程池对应的应用场景
                  • 案例
                  • 输出结果
                  相关产品与服务
                  云服务器
                  云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档