线程池学习(一)

转载:Java 线程池

线程池的创建方式

方式一:创建单一线程的线程池 newSingleThreadExecutor

特点:

  • 线程池中只包含 1 个线程,存活时间是无限的
  • 按照提交顺序执行任务
  • 唯一线程繁忙时,新提交的任务会被加入到阻塞队列中的,阻塞队列是无限的
  • 适用场景:要求任务按照提交次序执行时
 static class TargetTask implements Runnable {
        public static final int SLEEP_GAP = 1000;
        // AtomicInteger 提供了一种线程安全的方式来对整数进行操作。它可以保证多个线程同时对同一个AtomicInteger对象进行操作时,不会出现数据竞争和不一致的情况
        static AtomicInteger taskNo = new AtomicInteger(0);
        String taskName;

        @Override
        public void run() {
            log.info(taskName + "is running ...");
            try{
                Thread.sleep(SLEEP_GAP);
            }catch (Exception e){
                log.error(taskName + "running error !");
                e.printStackTrace();
            }
            log.info(taskName + "is  end ...");

        }
        TargetTask(){
            taskName = "task_" + taskNo;
            taskNo.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
//         方式一:创建单一线程的线程池 newSingleThreadExecutor
        ExecutorService poll1 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 2; i++) {
            poll1.execute(new TargetTask());
            poll1.submit(new TargetTask());
        }
        poll1.shutdown();
    }
  }

在这里插入图片描述

方式二:创建指定线程数量的线程池 newFixedThreadPool
  • 如果线程池中任务没有达到『固定数量』,每次提交任务线程池中就会创建新的线程。
  • 如果线程数量达到『固定数量』,新提交的任务就会加到阻塞队列中,并且阻塞队列是无界的。
  • 如果线程池中的线程因为执行任务而异常了,那么线程池中就会补一个新的线程
  • 适用场景:需要任务长期执行的场景,CPU 密集型场景
  • 缺点:当突增大量任务时,阻塞队列无限增大,服务器资源迅速耗尽
// 方式二:创建指定线程数量的线程池 newFixedThreadPool
        ExecutorService poll2 = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 2; i++) {
            poll2.execute(new TargetTask());
            poll2.submit(new TargetTask());
        }
        poll2.shutdown();

如图:最多只有3个线程同时进行
在这里插入图片描述

方式三:创建可缓存的线程池 newCachedThreadPool
  • 新增加任务时,如果线程池内的线程均繁忙,则会新增加线程来执行
  • 如果部分线程空闲,超过60s则会进行线程回收,则被终止并移出缓存
  • 适用场景:需要快速处理突发性强,耗时短的线程,如Netty的NIO处理场景、REST API接口的瞬时削峰场景
  • 缺点:当突增大量任务时,创建线程过多导致资源耗尽
ExecutorService poll3 = Executors.newCachedThreadPool();
        for (int i = 0; i < 2; i++) {
            poll3.execute(new TargetTask());
            poll3.submit(new TargetTask());
        }
        poll3.shutdown();

如图:只要有新的任务,就会开辟线程
在这里插入图片描述

方式四:创建可调度线程池 newScheduledThreadPool
  • 首次执行任务可以设置延迟时间
  • 具有周期性,主线程睡眠时间越长,线程池中的周期次数越多
ScheduledExecutorService poll4 = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 5; i++) {
            // 参数1: task任务
            // 参数2: 首次执行任务的延迟时间
            // 参数3: 周期性执行的时间
            // 参数4: 时间单位
            poll4.scheduleAtFixedRate(new TargetTask(),0,500, TimeUnit.MILLISECONDS);
        }
        Thread.sleep(4000); //主线程睡眠时间越长 周期次数越多
        poll4.shutdown();

如图:周期性的执行任务
在这里插入图片描述

方式五: ThreadPoolExecutor 标准的创建方式
  • 最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装
// 方式五: ThreadPoolExecutor 标准的创建方式,上面1-3 种方式都是ThreadPoolExecutor的变种
        // 参数1: 核心线程数量
        // 参数2: 最大线程数量
        // 参数3: 核心线程之外的空闲线程的存活时间
        // 参数4: 存活时间的单位
        // 参数5: 阻塞队列
        
        // 下面这种形式类似于方式二,创建固定数量的线程数
        ExecutorService poll5 = new ThreadPoolExecutor(3, 3,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());

        for (int i = 0; i < 2; i++) {
            poll5.execute(new TargetTask());
            poll5.submit(new TargetTask());
        }
        poll5.shutdown();

如图:最多启动3个线程执行任务
在这里插入图片描述

总结:

  • newFixedThreadPool和newSingleThreadExecutor: 阻塞队列无界,会堆积大量任务导致OOM(内存耗尽)
  • newCachedThreadPool和newScheduledThreadPool: 线程数量无上界,会导致创建大量的线程,从而导致OOM
  • 建议直接使用线程池ThreadPoolExecutor的构造器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于 Java 线程池导致 MySQL 的 Bug,可能是由于线程池中的线程数量过多,导致 MySQL 连接池被耗尽,从而出现连接超时或者连接泄露的情况。这种情况下,可以通过优化线程池的配置,增加 MySQL 连接池的大小,或者使用连接池管理工具进行监控和管理,来避免这种情况的发生。 至于关于线程池的问题,可以具体分为以下几个方面: 1. 线程池的大小:线程池的大小需要根据实际的业务场景来进行设置,如果线程池的大小过小,可能会导致任务无法及时处理,而过大则会占用过多的系统资源,影响系统的性能表现。 2. 线程池的类型:线程池的类型包括 FixedThreadPool、CachedThreadPool、ScheduledThreadPool 等,不同类型的线程池适用于不同的场景,需要根据实际的业务需求进行选择。 3. 线程池的拒绝策略:当线程池中的任务数量超过线程池的最大容量时,需要采取一定的拒绝策略,如 AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 等,需要根据业务场景和系统性能要求进行选择。 4. 线程池的生命周期管理:线程池的生命周期包括创建、启动、运行、停止等多个阶段,在使用线程池时需要对其进行合理的生命周期管理,以确保线程池的稳定运行和性能表现。 总之,线程池是一个非常重要的并发编程工具,需要在实践中不断学习和积累经验,以提高系统的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值