线程池的参数+创建线程池的方式(特点)

线程池的源码:

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

1. 线程池的7个参数

        1.1 corePoolSize

        即线程池中核心线程的数目,再没有指定maximumPoolSize所能执行的最大线程。若没有设置{@code allowCoreThreadTimeOut},空闲线程也不会清理。

        1.2 maximumPoolSize

        即线程池能够执行的最大线程数,当正在执行线程数大于等于corePoolSize的时候,可以通过设置最大线程池容量来执行新增线程。通过构造方法不难发现maximumPoolSize >= corePoolSize ,否则会抛出异常。

        1.3 keepAliveTime

        首先线程池中有空闲线程,但是当前线程已经超过了corePoolSize:可以通过keepAliveTime让空闲线程不会立即销毁,设置参数即还能存活的时间。

换就话说,当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。

        1.4 TimeUnit

        这个参数就是与前一个参数密切相关,TimeUnit就是时间单位,在这为keepAliveTime的单位,为java.util.concurrent包下的一个枚举类型,以下就是其中包含的基本类型(在这里并不是这个枚举类型的全部内容,有兴趣的可以去研究一下源码)。

public enum TimeUnit {
    /**
     * Time unit representing one thousandth of a microsecond.
     */
    NANOSECONDS(TimeUnit.NANO_SCALE),
    /**
     * Time unit representing one thousandth of a millisecond.
     */
    MICROSECONDS(TimeUnit.MICRO_SCALE),
    /**
     * Time unit representing one thousandth of a second.
     */
    MILLISECONDS(TimeUnit.MILLI_SCALE),
    /**
     * Time unit representing one second.
     */
    SECONDS(TimeUnit.SECOND_SCALE),
    /**
     * Time unit representing sixty seconds.
     * @since 1.6
     */
    MINUTES(TimeUnit.MINUTE_SCALE),
    /**
     * Time unit representing sixty minutes.
     * @since 1.6
     */
    HOURS(TimeUnit.HOUR_SCALE),
    /**
     * Time unit representing twenty four hours.
     * @since 1.6
     */
    DAYS(TimeUnit.DAY_SCALE);
}

        1.5 workQueue

        这是一个阻塞队列,就是当你的任务线程数>maximumPoolSize的时候,多余的线程就会就会进入这个堵塞队列等待执行。在这里提一下,为什么不用非阻塞队列:


阻塞队列和非阻塞队列的区别:见名知意,阻塞队列能够阻塞而非阻塞队列不能阻塞。具体点,当队列为空时进行出队操作,非阻塞队列会返回空或者抛出异常。而阻塞队列会等到直到有对象入队列才能出队。当队列满时也同理。(在这是不是很妙,细品。)

        1.6 threadFactory

                创建线程工厂类,将runnable接口创建为线程,在这里可以秀一下lamdal表达式,同时这里感觉和JDK1.8之后Stream中的Function接口很像。它的使用场景主要是设置线程优先级,守护状态等等

public interface ThreadFactory {
​
    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

        1.7 RejectedExecutionHandler

        最后一个就是拒绝策略,也就是当线程池和等待队列都满了,这个时候又来了新的线程,该怎么办?当然是拒绝,但该怎么拒绝?这个时候jak中提供了四个它的实现类来解决这个问题:

 他们依次的放弃原则是:

AbortPolicy(中止策略):当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程,默认策略

CallerRunsPolicy(调用者运行策略):当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理;

DiscardPolicy(丢弃策略):直接静悄悄的丢弃这个任务,不触发任何动作;

DiscardOldestPolicy(弃老策略):如果线程池未关闭,就弹出队列头部的元素,然后尝试执行;


好了说完了上面创建线程池的方式,我想大家看完肯定会有感慨,这么创建线程池不得累死。那我们继续来聊聊创建线程池常用的方式吧。

2.创建线程池的四种方式

        常用的创建线程池的方式都是基于Executors工具类来实现的。它们分别是:

        1. Executors.newFixedThreadPool

        创建固定大小的线程池。

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

        可控制线程最大并发数,超出的线程会在队列中等待。一般可以建议设置最大线程数为CPU的核数。

        2.Executors.newSingleThreadExecutor

        创建单一的线程池,能够保证线程按照顺序执行

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

        3.Executors.newCachedThreadPool;

        创建缓冲线程池。

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

        newCachedThreadPool 可以无限的创建线程,容易造成堆外内存溢出,因为它的最大值是在初始化的时候设置为 Integer.MAX_VALUE,一般情况不建议使用

        4.Executors.newScheduledThreadPool

        创建一个固定大小的线程池,该线程池可以安排命令在给定延迟后运行或定期执行。

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory){
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

总结

        不难发现创建线程池的四种方式,前三种还是基于ThreadPoolExecutor,而最后一种ScheduledThreadPoolExecutor也是ThreadPoolExecutor的子类,因此不难发现,线程池的创建离不开ThreadPoolExecutor。

**最近正在学习java,可能有些理解有偏差,欢迎指正。**

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值