java线程池创建线程过程及使用建议

java中经常需要用到多线程来处理一些业务,有很多人直接在使用多线程的时候直接继承Thread或者实现Runnable接口的方式来创建线程,但这种方式在创建及销毁线程耗费资源、线程上下文切换问题,同时创建过多的线程也可能引发资源耗尽的风险。这个时候引入线程池比较合理,方便线程任务的管理。
本文就线程池的线程创建过程进行demo分析及如何创建线程池给出一些建议。

先看一下Java线程池ThreadPoolExecutor的构造函数参数

public ThreadPoolExecutor(
  int corePoolSize, // 核心线程数,即最小线程数
  int maximumPoolSize, // 最大线程数,只有当workQueue队列填满时才会创建多于corePoolSize的线程
  long keepAliveTime, TimeUnit unit, // 非核心线程的空闲时间超过keepAliveTime就会被自动终止回收,corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程)
  BlockingQueue<Runnable> workQueue, // 任务的排队队列
  ThreadFactory threadFactory, // 新线程的产生方式
  RejectedExecutionHandler handler) // 拒绝策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

测试案例:

public class PoolTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 20; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() +" 开始执行-----------------"
                    );
                    Thread.sleep(10000L);
                }
            });
            System.out.println(Thread.currentThread().getName() +" :::"
                    + "--PoolSize:" +  threadPoolExecutor.getPoolSize()
                    + "--CorePoolSize:" + threadPoolExecutor.getCorePoolSize()
//                    +"--MaximumPoolSize:" + threadPoolExecutor.getMaximumPoolSize()
                    +"--TaskCount:" + threadPoolExecutor.getTaskCount()
                    +"--Queue.size:" + threadPoolExecutor.getQueue().size()
                    +"--线程池中活动线程数ActiveCount:" + threadPoolExecutor.getActiveCount()
                    +"--完成线程数CompletedTaskCount:" + threadPoolExecutor.getCompletedTaskCount()
            );
            Thread.sleep(500L);
        }

        System.out.println("======================================================");
        while (threadPoolExecutor.getActiveCount() >= 0) {
            System.out.println(Thread.currentThread().getName()
                    + "--PoolSize:" +  threadPoolExecutor.getPoolSize()
                    + "--CorePoolSize:" + threadPoolExecutor.getCorePoolSize()
                    +"--MaximumPoolSize:" + threadPoolExecutor.getMaximumPoolSize()
                    +"--Queue.size:" + threadPoolExecutor.getQueue().size()
                    +"--线程池中活动线程数ActiveCount:" + threadPoolExecutor.getActiveCount()
                    +"--完成线程数CompletedTaskCount:" + threadPoolExecutor.getCompletedTaskCount()
            );
            Thread.sleep(1000L);
        }
    }
}

运行结果:
pool-1-thread-1 开始执行-----------------
main :::–PoolSize:1–CorePoolSize:2–TaskCount:1–Queue.size:0–线程池中活动线程数ActiveCount:1–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:2–Queue.size:0–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
pool-1-thread-2 开始执行-----------------
main :::–PoolSize:2–CorePoolSize:2–TaskCount:3–Queue.size:1–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:4–Queue.size:2–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:5–Queue.size:3–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:6–Queue.size:4–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:7–Queue.size:5–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:3–CorePoolSize:2–TaskCount:8–Queue.size:5–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:0
pool-1-thread-3 开始执行-----------------
main :::–PoolSize:4–CorePoolSize:2–TaskCount:9–Queue.size:5–线程池中活动线程数ActiveCount:4–完成线程数CompletedTaskCount:0
pool-1-thread-4 开始执行-----------------
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
pool-1-thread-5 开始执行-----------------
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
pool-1-thread-1 开始执行-----------------

========================================
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:4–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:1
pool-1-thread-2 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
pool-1-thread-3 开始执行-----------------
pool-1-thread-4 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:1–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:4
pool-1-thread-5 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:4–完成线程数CompletedTaskCount:6
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:1–完成线程数CompletedTaskCount:9
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10
// 下面这行日志是过了60秒之后再从控制台粘贴出来的,说明每隔60秒如果没有新的任务,则线程池会对不活跃的线程进行销毁
main–PoolSize:4–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10

线程池创建线程过程结论:
1.线程池收到线程之后判断一下线程池的线程数poolSize是否小于CorePoolSize;如果小于,则新建一个线程,用于执行新提交的任务;
2.如果线程池的线程数poolSize大于CorePoolSize核心线程数,则把新提交的请求放入到ArrayBlockingQueue阻塞队列中;
3.1如果阻塞队列未满,则把新提交的任务加入到阻塞队列;(例如本例子提交5个线程,先执行了前面2个,然后把3-5个任务放到阻塞队列,这时候并不会创建新的线程执行任务)
3.2如果阻塞队列已满,且poolSize小于maximumPoolSize,则线程池新建一个线程用于处理新的任务;(例如本例子提交10个线程,先执行了前面2个,然后再执行第8个,第9,第10个任务;等到线程池的线程有空了再执行第3个,第4个;因为第3-7个任务已经进入了阻塞队列;这时候阻塞队列满了,线程池如果收到新的提交任务,则会执行执行新提交的任务)
3.3 如果阻塞队列已满,且poolSize大于等于maximumPoolSize。这时候新提交过来的线程在线程池中没有线程可以执行新的任务队列中也无法添加,则执行拒绝策略。

Executors创建线程池方法:

方法名功能
newFixedThreadPool(int nThreads)创建固定大小的线程池
newSingleThreadExecutor()创建只有一个线程的线程池
newCachedThreadPool()创建一个不限线程数上限的线程池,任何提交的任务都将立即执行

查看源码后发现这三个构造方法的底层都是直接用了ThreadPoolExecutor,只不过是不同实现的时候对ThreadPoolExecutor的构造参数做了特殊处理,如下所示。

newFixedThreadPool:
        构造方式为new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())
        问题:设置了corePoolSize=maxPoolSize,keepAliveTime=0(此时该参数没作用),无界队列,任务可以无限放入,当请求过多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致占用过多内存或直接导致OOM异常

newSingleThreadExector:
       构造方式为new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), var0)
       问题:基本同newFixedThreadPool,但是将线程数设置为了1,单线程,弊端和newFixedThreadPool一致

newCachedThreadPool:
        构造方式为new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue())
        问题:corePoolSize=0,maxPoolSize为很大的数,同步移交队列,也就是说不维护常驻线程(核心线程),每次来请求直接创建新线程来处理任务,也不使用队列缓冲,会自动回收多余线程,由于将maxPoolSize设置成Integer.MAX_VALUE,当请求很多时就可能创建过多的线程,导致资源耗尽OOM

结论:
不推荐直接使用Executors创建线程池的4种方法(如newFixedThreadPool),而是直接使用ThreadPoolExecutor来创建线程池,目的就是为了让开发者根据业务实际情况来创建合适的线程池,避免默认线程池的各种各样问题。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个Java创建线程池使用的示例代码: ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 创建线程池,最多同时执行2个任务 ExecutorService threadPool = Executors.newFixedThreadPool(2); // 添加任务到线程池 for (int i = 1; i <= 5; i++) { final int index = i; threadPool.execute(new Runnable() { public void run() { System.out.println("任务 " + index + " 开始执行,线程名为 " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任务 " + index + " 执行完毕,线程名为 " + Thread.currentThread().getName()); } }); } // 关闭线程池 threadPool.shutdown(); } } ``` 以上代码中,我们使用`Executors.newFixedThreadPool()`方法创建一个固定大小的线程池,最多同时执行2个任务。然后使用`execute()`方法添加5个任务到线程池中,每个任务都会打印出当前线程名,并让线程休眠1秒钟模拟任务执行的过程。最后使用`shutdown()`方法关闭线程池。 输出结果如下: ``` 任务 1 开始执行,线程名为 pool-1-thread-1 任务 2 开始执行,线程名为 pool-1-thread-2 任务 1 执行完毕,线程名为 pool-1-thread-1 任务 3 开始执行,线程名为 pool-1-thread-1 任务 2 执行完毕,线程名为 pool-1-thread-2 任务 4 开始执行,线程名为 pool-1-thread-2 任务 3 执行完毕,线程名为 pool-1-thread-1 任务 5 开始执行,线程名为 pool-1-thread-1 任务 4 执行完毕,线程名为 pool-1-thread-2 任务 5 执行完毕,线程名为 pool-1-thread-1 ``` 可以看到,线程池中最多同时执行2个任务,任务执行的线程名不固定,任务执行完毕后,线程会自动回收。这样可以避免频繁的创建和销毁线程的开销,提高程序的性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值