如何正确的使用线程池

 

 

一、jdk自带创建线程池

          1.Executors工具类本身给我们提供了几种创建线程的方法

 

          2.Executors.newCachedThreadPool();

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

核心线程数为0,

最大线程数为MAX_VALUE = 0x7fffffff;

60L:表示当线程池中当前线程数量大于核心线程数时,这时候队列中没有新的任务进来时,线程能够存活的最长时间

TimeUnit.SECONDS:时间单位秒,即60秒

new SynchronousQueue<Runnable>():任务有界队列,适用元素数量少的场景,适合做交换数据用

 

 

         3.Executors.newFixedThreadPool(nThreads);

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

自定义线程池中线程的个数,核心线程数和最大线程数一致。

核心线程数在线程池初始化时便创建出来了,所以这里不会因为有过多的任务而创建新的线程,也不会去销毁多余的线程,所以等待存活时间设置为0L,

new LinkedBlockingQueue<Runnable>():有界阻塞队列,底层链表实现,添加和删除元素使用的不是同一个锁。

public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

但是看jdk源码中,在我们不传入capacity时,默认队列的大小为MAX_VALUE = 0x7fffffff,相当于是一个无界队列了。

 

         4.Executors.newScheduledThreadPool(corePoolSize);

 public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

调用的是父类的方法,继续看一下父类的具体实现。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

继续追中this这个方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

其实这个方法也就是对参数做了一个判断,然后赋值给成员变量,我们要看的还是前面那块代码,传入的值是多少

最大线程数还是MAX_VALUE = 0x7fffffff;

多余线程空闲等待时间为0;

new DelayedQueue() 延时队列,无界队列,

 

     5.Executors.newSingleThreadExecutor(threadFactory);

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

这个线程池的核心线程数和最大线程数都为1:

 

 

二、jdk自带创建线程池的一个总结

 

线程池核心线程数最大线程数任务队列
newCachedThreadPool()0Intger.MAX_VALUESynchronousQueue<Runnable>()(有界)
newFixedThreadPool(nThreads)用户自定义用户自定义LinkedBlockingQueue<Runnable>()(无界)
newScheduledThreadPool(corePoolSize)用户自定义Intger.MAX_VALUEDelayedQueue()(有界)
newSingleThreadExecutor(threadFactory)11LinkedBlockingQueue<Runnable>()(无界)

 

 

 

 

 

 

1.缺陷

   上面创建的几种线程池它们的缺陷在哪里呢。

   首先线程池的最大线程数肯定不能为MAX_VALUE,因为我们的cpu是有限的,所以当线程数达到上限时,还继续创建线程,势必会对服务器造成影响,

  然后,有几个采用的是无界队列,无界队列,意味着当我们任务数足够多时,会一直往这个队列里面存放,点那个存放上限超过内存容量时,势必也会造成内存溢出。

  这几种风险都是在生产上不能不去考虑的,所以我们需要自己定义线程池。

 

 

 、自定义线程池

    先贴代码

public static void main(String[] args) {
		ThreadPoolExecutor threadPool=new ThreadPoolExecutor(
				//核心线程数
				10, 
				//最大线程数
				Runtime.getRuntime().availableProcessors()*2, 
				//超过核心线程数,空闲线程最长等待时间
				60L, 
				//单位秒
				TimeUnit.SECONDS, 
				//有界阻塞队列,读写用同一个锁
				new ArrayBlockingQueue<Runnable>(1000), 
				//自定义创建线程的工厂
				new ThreadFactory() {
					@Override
					public Thread newThread(Runnable r) {
						Thread thread=new Thread(r);
						thread.setName("students");
						if(thread.isDaemon()) {
							thread.setDaemon(false);
						}
						if(Thread.NORM_PRIORITY!=thread.getPriority()) {
							thread.setPriority(Thread.NORM_PRIORITY);
						}
						return thread;
					}
				}, 
				//自定义拒绝策略
				new  RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println("任务丢失,打印日志");
					}
				});
	}

 

1.核心线程数

    这里核心线程数设置为了10个,一般根据自己业务的需求来决定,这个值不要过大。

2.最大线程数

     

任务类型最大线程数
计算机(cpu)密集型   Runtime.getRuntime().availableProcessors()*2或者Runtime.getRuntime().availableProcessors()+1
io密集型   Runtime.getRuntime().availableProcessors()/(1-阻塞系数 )         (阻塞系数(0.8~0.9))

 

Runtime.getRuntime().availableProcessors():cpu个数

注意:在一个项目中,有时候往往不止用到了一个线程池。所以在设置这个值的时候也要考虑其他线程池的个数。 

3.任务队列

    ArrayBlockingQueue<Runnable>(1000)有界队列,防止内存溢出。

4.线程工厂

    自定义线程工厂,可以设置线程的一些属性,如设置名称,在查看线程信息的时候,能够很好的区分哪些线程是用来干嘛的,

    可以将线程设置为非守护线程,防止线程一直在后台挂起。

    将优先级还原。

5.拒绝策略

    当线程池中没有空闲线程时,线程数量为最大线程数,任务还在不停的往队列里面添加,势必一些任务我们会拒绝执行。对于这些拒绝执行的任务可以将它的日志打印出来,或者做一些其他的处理。

 

四、总结

   1.工作生产上,一定使用自定义线程池。

   2.注意线程最大数量和任务队列的设置。

   3.有良好的拒绝策略

 

 

获取更多学习资料,面试题以及视频,关注微信公众号:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值