线程池的使用和分析jdk1.8

版权声明: https://blog.csdn.net/ZHENZHEN9310/article/details/86247789

Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。

线程池如何实现线程的复用:
通过BlockingQueue阻塞队列,存放Runnable对象,检查线程队列中的线程,存在线程空闲且未被销毁,继续使用此线程执行任务。

使用线程池的优点:

  • 1、重用已经创建好的线程,避免频繁创建和销毁的系统开销
  • 2、控制线程并发数,合理使用系统资源,提高应用的性能
  • 3、手动管理线程,比如定时执行、取消执行等

1、Executors的使用

public class ExecutorDemo {

    public static void main(String[] args)  {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executor.execute(new Task());
        }
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

2、ThreadPoolExecutor的介绍

ThreadPoolExecutor构造函数:

  • corePoolSize 核心线程数,当提交一个任务(Runnable对象),只要当前线程数小于corePoolSize,就会创建一个线程。默认情况下,核心线程即使处于空闲状态,也会保持存活状态。如果将ThreadPoolExecutor的allowCoreThreadTimeout设置为true,则核心线程也会存在超时策略,当等待时间超过keepAliveTime,核心线程就会结束。

  • blockingQueue 维护一个runnable对象的阻塞队列,当队列为空的时候,此时取出任务的操作会被阻塞;在满队列的时候,添加操作同样被阻塞。不同的blockingQueue对线程池运行逻辑有很大影响,可以选择以下几个阻塞队列:

        ArrayBlockingQueue:基于数组结构的有界阻塞队列。
        LinkedBlockingQueue:一个基于链表结构的无界阻塞队列。
        SynchronousQueue:一个不存储元素的阻塞队列。
        PriorityBlockingQueue:一个具有优先级的无界阻塞队列。
    
  • keepAliveTime 线程执行结束后,保持存活的时间。

  • TimeUnit: keepAliveTime的时间单位,可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。TimeUnit工具类

  • ThreadFactory: 用于设置创建线程的工厂,通过线程工厂给线程设置名字。

  • RejectedExecutionHandler: 线程池队列饱和之后的执行策略,默认是采用AbortPolicy。JDK提供四种实现方式:

      AbortPolicy:直接抛出异常
      CallerRunsPolicy :只用调用者所在线程来运行任务
      DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务
      DiscardPolicy : 不处理,丢弃掉
    
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;
    }

3、Executors

Excutors是个工厂类,里面提供多了创建多种不同线程池的方法,常用的有 :

3.1 newFixedThreadPool

创建固定数目的核心线程数,LinkedBlockingQueue为无界的阻塞队列,即使线程都处于运行状态,还可以不断的往blockingQueue中插入runnable对象。

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

3.2 newSingleThreadExecutor

有且只有一个核心线程,LinkedBlockingQueue为无界的阻塞队列,即使线程都处于运行状态,还可以不断的往blockingQueue中插入runnable对象。

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

3.3 newSingleThreadExecutor

无核心线程,可以创建N个非核心线程;在线程任务执行完,60秒后,如果没有新的任务到达,则线程消亡;SynchronousQueue插入操作必须等上一个元素被移除之后,否则插入操作一直处于阻塞状态。

如果线程仍然存活且入队成功,则复用线程进行执行;否则创建新的线程;如果都失败,reject(runnable)

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

3.4 newScheduledThreadPool

定时任务,可以指定initialDelay或者周期性定时任务

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

4、ThreadPoolExecutor

ThreadPoolExecutor # execute(runnable)

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
  • 1、如果目前运行线程数少于核心线程数,addWorker(command,true)

  • 2、如果线程还在运行,且command入队成功,进行double-check,以免在上次检查之后线程已挂或者线程池已挂。如果已挂,则reject(command);如果线程池中线程数目为0,则还没有创建过线程,addWorker(null, false)

  • 3、如果以上两步均不满足,addWorker(command, false) 尝试新建非核心线程。创建失败,则reject(command)

细节分析

  • addWorker(command,true) 创建一个带command的核心线程
  • addWorker(null, false) 创建一个command为空的非核心线程,尔后会通过getTask()从workQueue中取出一个runnable对象执行
  • addWorker(command, false) 创建一个带command的非核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask); //实例化worker对象
            final Thread t = w.thread; //获得worker对象中的thread
            if (t != null) {
                if (workerAdded) {
                    t.start();
                }
            }
        } 
        return workerStarted;
    }

Worker

addWorker()中会new Worker(command),实例化worker对象成功,执行work.thread.start()

thread.start() -> runnable.run() -> runWorker(worker)

最终执行传入的这个command对象

 private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
        final Thread thread;
        Runnable firstTask; //线程执行的第一个task,可能为null
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * firstTask赋值,并通过threadFactory创建线程
         * 将当前worker对象作为runnable对象用作thread初始化
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** 执行thread.start() -> runnable.run() -> runWorker() */
        public void run() {
            runWorker(this);
        }

    }
 final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask; //task可能为null
        w.firstTask = null;
        try {
            while (task != null || (task = getTask()) != null) {
                        task.run(); //最终执行command
                        
                        task = null; //清除task
            }
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
 private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { 
            //如果线程存活且workQueue是个空队列,返回null
                return null;
            }
            //如果线程已经超时,仍没有任务到达,返回null
            int wc = workerCountOf(c);
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; 
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
               
        }
    }

细节:

1、在task为null的状态,就得通过getTask不断的从workQueue中取出task

1、execute(command)中,addWorker(null, false)
2、执行完task.run(),task = null,清除状态

2、getTask()返回runnable对象

timed为true,受keepAliveTime限制,有超时机制;否则无超时机制,无限等待

也就是这里设置了核心线程会一直存活,非核心线程在超时后会消亡

1、如果是核心线程且allowCoreThreadTimeOut为false, workQueue.take(),一直等待直到任务available

2、如果是非核心线程,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 带超时机制,
如果超时还没有任务到达,则返回null,

参考

java并发编程–Executor框架

Java线程池实现原理与源码解析(jdk1.8)

Java 线程池ThreadPoolExecutor(基于jdk1.8)

猜你喜欢

转载自blog.csdn.net/ZHENZHEN9310/article/details/86247789