Java并发编程八 线程池

Java并发编程八 线程池

1.线程池作用

1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2.提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3.提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

2. 线程池ThreadPoolExecutor

我们可以通过ThreadPoolExecutor类来自定义线程池。
首先看ThreadPoolExecutor的构造方法。

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

2.1 corePoolSize(核心线程数)

当提交一个任务到线程池时,如果当前线程池存活的线程数小于corePoolSize则创建一个新的线程执行任务。
如果当前线程数量大于等于corePoolSize 则不会继续创建核心线程而是将任务存入到BlockingQueue中。
如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。

2.2 maximumPoolSize(最大线程数)

线程池允许创建的最大线程数。如果阻塞队列BlockingQueue满了,并且当前的线程数量小于最大线程数时,线程池会继续创建非核心线程。直到线程池中的线程数量等于maximumPoolSize就不会再继续创建非核心线程了。

2.3 keepAliveTime(线程空闲时存活时间)

该线程池中非核心线程闲置超时时长
一个非核心线程,闲置状态的时长超过这个参数所设定的时长,就会被销毁掉
如果设置allowCoreThreadTimeOut = true,则会作用于核心线程

2.4 unit (存活时间单位)

keepAliveTime的单位,TimeUnit是一个枚举类型。

2.5 workQueue (阻塞队列)

当提交任务时,核心线程数量达到corePoolSize时,会将任务存入阻塞队列中。
阻塞队列有以下几种
ArrayBlockingQueue(有界队列)
有界队列,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则进行拒绝策略。

LinkedBlockingQueue(无界队列)
无界队列,接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize。
SynchronousQueue(同步队列)
同步队列,一种没有缓冲的队列,生产者生产的数据会直接被消费者获取并消费。
DelayQueue(延时队列)
队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。

2.6 threadFactory (线程工厂)

创建线程的方式,一般很少自己实现。

2.7 handler (拒绝策略)

当阻塞队列满了并且线程池中线程数已经达到最大值时,说明线程池处于饱和状态。如果再有任务进入线程池中则会调用拒绝策略进行拒绝任务。
AbortPolicy,直接抛出异常系统继续正常工作。
CallerRunsPolicy:只要线程未关闭,该策略直接在调用者线程中,运行当前被丢失的任务(别拒绝的策略不会在线程池中的线程内执行,而是在开启线程池的线程中执行,也就是main线程)。
DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务。
DiscardPolicy:丢弃无法处理的任务,不给任何处理。
当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

线程池执行任务流程图
流程图

2.Executors类

Jdk并发包给我们提供了四种常用的创建线程池的方法。

newFixedThreadPool

创建一个固定数量的核心线程池方法。
核心线程数与最大线程数量相等
空闲存活时间为0(无意义,因为没有非核心线程)
阻塞队列为无界队列
当任务提交到线程池中,会创建nThreads个线程执行任务,如果超出会将所有任务存入到无界队列内。

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

newCachedThreadPool

缓存线程池
核心线程为0
最大线程为Integer.MAX_VALUE
存活时间为60秒

当任务提交到线程池中会创建线程,如果线程空闲超过60秒会销毁空闲线程。

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

newSingleThreadExecutor

一个线程的线程池

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

newScheduledThreadPool

定长线程池:可以延迟执行任务或者周期性的执 行某个任务

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

其静态方法返回ScheduledExecutorService 类型的对象,ScheduledExecutorService接口有四个方法进行提交任务
这里写图片描述

schedule方法:延时执行一次任务

scheduleWithFixedDelay方法:以上一个任务执行完以后的固定时间周期执行

scheduleAtFixedRate方法:以固定时间周期执行,不管上一个线程是否执行完毕。

3.ExecuterService接口

通过创建连接池返回 ExecuterService接口类型的对象 ,而不得不说ExecuterService接口中的几个方法

3.1 void execute(Runnable command);

execute方法,只是执行传入实现Runnable接口的实例对象,并没有返回值。

3.2 Future submit(*);

submit方法有三种形式,参数不同。但是返回值都是FutureTask类的实例对象。
查看源码可知:

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

其实内部都new 一个FutureTask实例对象返回。
再次查看FutureTask源码的构造函数

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    // Executors.callable(runnable, result); 源码最终调用结果
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

从源码中可以看出
submit(Runnable task,T result) 只是将task执行并且返回传入的result值。
而submint(Callable task) 会返回我们实现call()方法的return的值。

猜你喜欢

转载自blog.csdn.net/oYueYang1/article/details/80336977