ThreadPoolExecutor thread pool

use:

Automation for managing threads, developers only need to focus on business implementation, without paying attention to the management of threads, reduce development requirements

Method explanation:

Executor
 //perform the task (if the task execution thread has the task queue waiting to enter the worker thread pull) no return value
voidthe Execute (Runnable the Command); ExecutorService



 //close the thread pool is no longer receiving a new task but the task queue still waiting After execution will
voidthe shutdown ();

//Close the thread pool is no longer received new task queue and the task will not be executed
List <the Runnable>shutdownNow ();

//if the closed state of the thread pool return
BooleanisShutdown ();

//returns the thread is whether the end of the run only after the shutdown and shutdownNow, and have returned true ending will perform
booleanisTerminated ();

//wait for the end of the implementation of
booleanawaitTermination (Longtimeout, TimeUnit Unit)throwsInterruptedException;

//submit the task Similarly execute () returns the difference is the result of objects
<T> Future <T> Submit (a Callable <T> Task); 

// submit the task if the task execution completion of a given return current Result 
<T> Future <T> Submit (the Runnable Task, Result T); 

// Submit Task basic and execute () method, even though there are return Future, but the reality is Future <Void>, no return value <?> 
Future <?> the submit (Runnable task); 

// batch job submission and all tasks will still enter the waiting task Queuing returns all results in this batch after completion of all tasks performed 
<T> List <Future <T >> invokeAll (Collection <? the extends a Callable <T >> tasks) throws InterruptedException; 

// batch job submission and all tasks will still enter the task but the waiting queue has exceeded the time limit if there are not given time to perform tasks directly returns all results (this time there may be some no Future results) 
<T> List <Future <T >> invokeAll (Collection <? the extends Callable <T>> tasks,long timeout, TimeUnit unit) throws InterruptedException; 

// batch job submission and all tasks will still enter the queue as long as there is a job waiting for the completion of execution immediately returns the result of the implementation of the tasks completed 
<T> T invokeAny (Collection <? the extends a Callable <T >> Tasks) throws InterruptedException , ExecutionException; 

// batch job submission and all tasks will still enter the queue as long as there is a job waiting for the completion of execution immediately returns the results of the task execution is complete absence of any return timeout exception is thrown 
<T> T invokeAny (Collection <? the extends a Callable <T >> Tasks, Long timeout, TimeUnit Unit) throws InterruptedException, ExecutionException, a TimeoutException;

 

Example of use:

execute(Runnable command)

public static void main(String[] args) throws Exception {
    ExecutorService executorService = new ThreadPoolExecutor(1, 10,0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
    for (int i=1; i<5; i++){
        int finalI = i;
        executorService.submit(()->{
            System.out.println("打印"+finalI);
        });
    }
}
结果:
打印1
打印2
打印3
打印4

submit(Callable<T> task)

//提交任务 类似execute() 区别是有返回结果对象 public static void main(String[] args) throws Exception {
        ExecutorService executorService = new ThreadPoolExecutor(1, 10,0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    List<Future<Integer>> futureList = Lists.newArrayList();
    for (int i=1; i<5; i++){
        int finalI = i;
        Future<Integer> future = executorService.submit(new Callable<Integer>() {
                @Override 
                public Integer Call () throws Exception { 
                    the Thread.sleep ( 200 is );
                     return Finali; 
                } 
            }); 
        futureList.add (Future); 
    } 
    // wait for all tasks are performed submitted complete 
    the Thread.sleep (5000 );
     for (Future <Integer> Future: futureList) { 
        System.out.println ( "returns:" + Future.get ()); 
    } 
} 
result: 
returns: 1 
returns: 2 
returns: 3 
returns: 4

shutdown()

public static void main(String[] args) throws Exception {
    ExecutorService executorService = new ThreadPoolExecutor(1, 10,0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
    for (int i=1; i<5; i++){
        int finalI = i;
        executorService.submit(()->{
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace (); 
            }
            System.out.println ( "Print" + Finali); 
        }); 
    } 
    executorService.shutdown (); 
    System.out.println ( "initiating shutdown request thread pool" );
     // wait for thread pool can After performing 
    the Thread.sleep (5000 ); 
    System.out.println (executorService.isShutdown ()); 
} 
results: 
thread pool initiate shutdown request 
printing 1 
printing 2 
printing 3 
printing. 4 
to true

shutdownNow()

public static void main(String[] args) throws Exception {
    ExecutorService executorService = new ThreadPoolExecutor(1, 10,0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
    for (int i=1; i<5; i++){
        int finalI = i;
        executorService.submit(()->{
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace (); 
            }
            System.out.println ( "Print" + Finali); 
        }); 
    } 
    ExecutorService.shutdownNow (); 
    System.out.println ( "initiating shutdownNow request thread pool" );
     // wait for thread pool can After performing 
    the Thread.sleep (5000 ); 
    System.out.println (executorService.isShutdown ()); 
} 
results: 
thread pool request initiated shutdownNow 
java.lang.InterruptedException: SLEEP interrupted 
    AT java.lang.Thread.sleep (Native Method,) 
    $ $ com.moredian.trade.biz.SocketServerTest.lambda main AT 0 (SocketServerTest.java:138 ) 
    AT java.util.concurrent.Executors $ RunnableAdapter.call (Executors.java: 511 )
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
打印1
true

Class Diagram:

Data structure diagram:

Delivery task may be an example cited the following figure Runnable Runnable Callable from delivery, to enter the queue, and then the worker thread consumption is

 

 

Workflow Description:

1 to submit the task, exucude, submit, invokeAll, invokeAny

2 Gets the current number of worker threads compared with the number of kernel threads, if the core number of threads is greater than the current number of worker threads add a new worker thread until the number of kernel threads equal to the number of worker threads

3 worker threads After processing the task queue to get a new task to perform the task, if the task queue task is not to block the current worker thread (which is why the reasons do not need to be repeated to create a thread, whether permanently blocked mainly to see keepAlive ) , until a new task into the task queue

4 随着任务提交, 当等待队列已满时,若满足 条件1: 工作线程都在执行中, 条件2: 最大线程数(maximumPoolSize)大于核心线程 此时开辟新的工作线程, 直至工作线程等于最大线程数

5 当所有工作线程都参与任务处理,任务队列仍满队列时,新的任务将抛弃不执行

6 随着任务的执行, 已经没有新的任务进来, 工作线程中将只保留核心(阻塞等待), 非核心线程将被释放

 

关键源码分析:

submit(Runnable task)

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
//执行任务
public void execute(Runnable command) {
    //若执行任务为空 抛出异常
    if (command == null)
        throw new NullPointerException();
    //获取当前ctl的值 ctl中是高3位作为状态值,低28位作为线程总数值
    //有对应的位运算来获取 一值多用
    int c = ctl.get();
    //若工作中线程数小于核心线程数 则增加一个工作线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //当前线程池是否处于运行状态 且 将任务投递到任务队列是否成功(如果任务队列限定最大长度 则offer返回fase)
    if (isRunning(c) && workQueue.offer(command)) {
        //再次获取ctl的值
        int recheck = ctl.get();
        //若非线程池非运行中(可能是shutdown了), 将当前任务从等待队列中移除 刚放进去就移除
        if (! isRunning(recheck) && remove(command))
            //拒绝此任务
            reject(command);
        //若工作线程数为0 增加一个工作线程 
        //这段代码看起来比较奇怪, 发生在核心线程也能被回收的场景(创建线程池时可以设置)
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //增加工作线程失败 拒绝任务
    else if (!addWorker(command, false))
        reject(command);
}
//添加一个工作线程
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        //获取当前ctl值
        int c = ctl.get();
        //获取线程池运行状态
        int rs = runStateOf(c);
        //下面这句代码非常难理解对照 !(1==1 && 2==2 && 3==3) == (1!=1 || 2!=2 || 3!=3)
        //将其改写 if(rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()))
        //rs> SHUTDOWN表示线程池非运行状态
        //总的来说就是如果非运行中 且 (状态不为shutdown 或 当前任务不等于空 或 工作队列为空)
        if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            return false;

        for (;;) {
            //当前工作线程数
            int wc = workerCountOf(c);
            //如果工作线程数大于容量 或者 
            //判断是否为核心线程 1若是核心线程不能大于核心线程总数 2若是非核心线程则不能大于最大线程数
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //工作线程数+1 并且跳出循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            //若此时运行状态发生改变 则继续循环
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    //以下代码为 compareAndIncrementWorkerCount(c) = true的前提下 并且ctl值已经发生改变 运行线程数已经加1
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //新增一个工作线程
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            //全局锁
            mainLock.lock();
            try {
                //当前运行状态
                int rs = runStateOf(ctl.get());
                //工作线程集合workers增加一个工作线程
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            //工作线程添加成功则启动当前工作线程 
            if (workerAdded) {
                //开始从任务队列取任务执行
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

维持线程池中工作线程不释放原理

从新建worker开始 new Worker(firstTask),  下面代码重点关注标红字体, 原理是利用阻塞队列调用 poll和take 方法时, 若取不到值则会阻塞当前前程,直至阻塞队列重新添加数据(实现原理是基于AQS 可以看我之前 ReentrentLock Condition文章)

//线程保持原因
//new Worker(firstTask); 
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    //重点关注newThread(this)
    this.thread = getThreadFactory().newThread(this);
}
//newThread(this) 那么直接找ThreadPoolExecutor的run()方法
public void run() {
    //往下看
    runWorker(this);
}
//运行worker 其他的不看 只看getTask()
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

//从任务队列中获取任务
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);

        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            //关键点在这里 workQueue的方法poll和take都是阻塞方法 若取不到值则当前工作线程阻塞
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

总结:

线程池能保持线程持续运行的关键在于阻塞队列的使用, 主要做的是何时阻塞队列 何时把线程从阻塞中释放出来,  亮点是ctl一值多用  

Guess you like

Origin www.cnblogs.com/xieyanke/p/12176515.html