`
iamzhongyong
  • 浏览: 796373 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

threadPoolExecutor代码

    博客分类:
  • java
 
阅读更多

java.util.concurrent包的出现,为我们实现多线程并发编程带来了很大的方便,但是同时也有很多问题,如果对于原理缺乏了解,使用起来可能就会有一些点忽略掉,导致应用出现问题,关于线程池已经想看源代码很长时间了,不能再拖下去了,本来打算是从头自己看源代码,但是发现一个哥们的博客,上面讲的挺详细,于是打算借鉴一些过来,然后加入自己的理解,不再重复造轮子了。

 

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

5newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

 

 

首先看ThreadPoolExecutor的构造函数中的核心,对于各个参数有必要介绍一下。

 

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;
    }
 

 

1corePoolSizecore zise 大家都懂得,不解释

2maximumPoolsize:线程池的最大数量

3keepAliveTime:空闲的线程等待工作的最长时间,纳秒,和unit配合使用

4woreQueue:线程池处理的任务队列

5threadFactory:线程创建的工厂类,threadPoolExecutor中的有默认实现,开始是最好自己实现一个,并且命名,方便问题排查

6handler:线程池的拒绝策略,默认有四种可选择,也可自己写一个

从两个if判断里面能够看出参数缺一不可,keepAliveTime的时间通过TimeUnit转为纳秒。

 

 

 

 

ThreadFactory是一个接口,里面就一个方法,Thread newThread(Runnable r) 用来创造线程

Executors中有一个默认的实现:DefaultThreadFactory

 

static class DefaultThreadFactory implements ThreadFactory {
        static final AtomicInteger poolNumber = new AtomicInteger(1);
        final ThreadGroup group;
        final AtomicInteger threadNumber = new AtomicInteger(1);
        final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null)? s.getThreadGroup() :
                                 Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
 

 



通过默认的factory创造的线程,是非守护,并且优先级为NORM_PRIORITY,线程的名字为“pool-线程池数目-thread-线程的编号”。

对于线程池中的所有线程默认都转换为非后台线程,这样主线程退出时不会直接退出JVM,而是等待线程池结束。还有一点就是默认将线程池中的所有线程都调为同一个级别,这样在操作系统角度来看所有系统都是公平的,不会导致竞争堆积。

 

线程的状态

为了节省资源和有效释放资源关闭一个线程池就显得很重要。线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行。因此关闭线程池可能出现一下几种情况:

1)平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务

2)立即关闭:取消所有正在执行和未执行的任务

线程池的状态,在源代码里面有以下注释,感觉比任务文章来的给力点呵呵

RUNNING

接收新的任务,同时处理队列中已有的任务

SHUTDOWN

不再接受新的任务,但是处理队列中的任务

STOP

不接受新任务,不处理队列中任务,同时终止正在运行的任务

TERMINATED

stop一样,但是所有的线程终止

RUNNINGSHUTDOWN:调用shutdown()方法

RUNNING  or SHUTDOWN STOP :调用shutdownNow()方法

SHUTDOWN TERMINATED 任务队列和线程池都空时

STOP TERMINATED :线程池为空时

 

 

 

 

     线程池的拒绝策略

这四种策略是独立无关的,是对任务拒绝处理的四中表现形式。最简单的方式就是直接丢弃任务。但是却有两种方式,到底是该丢弃哪一个任务,比如可以丢弃当前将要加入队列的任务本身(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还可以直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,但是由于抛出一个RuntimeException,因此会中断调用者的处理过程。除了抛出异常以外还可以不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。

四种策略均实现了RejectedExecutionHandler接口

方法rejectedExecution()在线程池不能接收任务的时候会被ThreadPoolExecutor调用,参数runnable是需要被执行的任务,executor是线程池对象。

public interface RejectedExecutionHandler {

    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

}

 

除了上述的几种拒绝策略外,还可以自己实现RejectedExecutionHandler接口,例如如下拒绝策略,当任务没法加入任务对队列时,阻塞,直到可以放入队列中。

 

ejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable task,ThreadPoolExecutor executor) {
           // 如果处理不过, 打日志, 并阻塞,
           logger.error("The thread pool is full and be blocked!"
                 + " corePoolSize=" + executor.getCorePoolSize()
                 + ", maxPoolSize=" + executor.getMaximumPoolSize()
                 + ", keepAliveTime="
                 + executor.getKeepAliveTime(TimeUnit.SECONDS)
                 + ", poolSize=" + executor.getPoolSize()
                 + ", activeCount=" + executor.getActiveCount()
                 + ", taskCount=" + executor.getTaskCount()
                 + ", largestPoolSize=" + executor.getLargestPoolSize()
                 + ", completedTaskCount="
                 + executor.getCompletedTaskCount());
 
           try {
              executor.getQueue().put(task);
           } catch (InterruptedException e) {
              logger.error("put the task into queue is failure!", e);
           }
        }
     };
 

 

 

 

  关于Worker对象

对于ThreadPoolExecutor而言,一个线程就是一个Worker对象,它与一个线程绑定,当Worker执行完毕就是线程执行完毕。

 

 /**
         * Runs a single task between before/after methods.
         */
        private void runTask(Runnable task) {
            final ReentrantLock runLock = this.runLock;
            runLock.lock();
            try {
                /*
                 * Ensure that unless pool is stopping, this thread
                 * does not have its interrupt set. This requires a
                 * double-check of state in case the interrupt was
                 * cleared concurrently with a shutdownNow -- if so,
                 * the interrupt is re-enabled.
                 */
                if (runState < STOP &&
                    Thread.interrupted() &&
                    runState >= STOP)
                    thread.interrupt();
                /*
                 * Track execution state to ensure that afterExecute
                 * is called only if task completed or threw
                 * exception. Otherwise, the caught runtime exception
                 * will have been thrown by afterExecute itself, in
                 * which case we don't want to call it again.
                 */
                boolean ran = false;
                beforeExecute(thread, task);
                try {
                    task.run();
                    ran = true;
                    afterExecute(task, null);
                    ++completedTasks;
                } catch (RuntimeException ex) {
                    if (!ran)
                        afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                runLock.unlock();
            }
        }

        /**
         * Main run loop
         */
        public void run() {
            try {
                Runnable task = firstTask;
                firstTask = null;
                while (task != null || (task = getTask()) != null) {
                    runTask(task);
                    task = null;
                }
            } finally {
                workerDone(this);
            }
        }
 

 

执行一个给定的任务,任务可能会在一个新的线程中执行,也有可能在线程池中已近存在的线程中执行。如果任务不能被提交执行(可能是因为线程池shutdown或者任务队列已经满了),此时这个任务会被handle来处理

 

 

几种常见的死锁

1)锁顺序死锁(lock-ordering deadlock):多个线程试图通过不同的顺序获得多个相同的资源,则发生的循环锁依赖现象。

2)动态的锁顺序死锁(Dynamic Lock Order Deadlocks):多个线程通过传递不同的锁造成的锁顺序死锁问题。

3)资源死锁(Resource Deadlocks):线程间相互等待对方持有的锁,并且谁都不会释放自己持有的锁发生的死锁。也就是说当现场持有和等待的目标成为资源,就有可能发生此死锁。这和锁顺序死锁不一样的地方是,竞争的资源之间并没有严格先后顺序,仅仅是相互依赖而已。

锁顺序死锁

最经典的锁顺序死锁就是LeftRightDeadLock.

 

public class LeftRightDeadLock {
    final Object left = new Object();
    final Object right = new Object();
    public void doLeftRight() {
        synchronized (left) {
            synchronized (right) {
                execute1();
            }
        }
    }
    public void doRightLeft() {
        synchronized (right) {
            synchronized (left) {
                execute2();
            }
        }
    }
    private void execute2() {
    }
    private void execute1() {
    }
}
 

 

 

这个例子很简单,当两个线程分别获取到leftright锁时,互相等待对方释放其对应的锁,很显然双方都陷入了绝境。

动态的锁顺序死锁

与锁顺序死锁不同的是动态的锁顺序死锁只是将静态的锁变成了动态锁。 一个比较生动的例子是这样的。

 

public void transferMoney(Account fromAccount,//
        Account toAccount,//
        int amount
        ) {
    synchronized (fromAccount) {
        synchronized (toAccount) {
            fromAccount.decr(amount);
            toAccount.add(amount);
        }
    }
}
 

 

当我们银行转账的时候,我们期望锁住双方的账户,这样保证是原子操作。 看起来很合理,可是如果双方同时在进行转账操作,那么就有可能发生死锁的可能性。

 

很显然,动态的锁顺序死锁的解决方案应该看起来和锁顺序死锁解决方案差不多。 但是一个比较特殊的解决方式是纠正这种顺序。 例如可以调整成这样:

 

Object lock = new Object();
public void transferMoney(Account fromAccount,//
        Account toAccount,//
        int amount
        ) {
    int order = fromAccount.name().compareTo(toAccount.name());
    Object lockFirst = order>0?toAccount:fromAccount;
    Object lockSecond = order>0?fromAccount:toAccount;
    if(order==0){
        synchronized(lock){
            synchronized(lockFirst){
                synchronized(lockSecond){
                    //do work
                }
            }
        }
 
    }else{
        synchronized(lockFirst){
            synchronized(lockSecond){
                //do work
            }
        }
    }
}
 

 

这个挺有意思的。比较两个账户的顺序,保证此两个账户之间的传递顺序总是按照某一种锁的顺序进行的, 即使多个线程同时发生,也会遵循一次操作完释放完锁才进行下一次操作的顺序,从而可以避免死锁的发生。

资源死锁

资源死锁比较容易理解,就是需要的资源远远大于已有的资源,这样就有可能线程间的资源竞争从而发生死锁。 一个简单的场景是,应用同时从两个连接池中获取资源,两个线程都在等待对方释放连接池的资源以便能够同时获取 到所需要的资源,从而发生死锁。

资源死锁除了这种资源之间的直接依赖死锁外,还有一种叫线程饥饿死锁(thread-starvation deadlock)。 严格意义上讲,这种死锁更像是活跃度问题。例如提交到线程池中的任务由于总是不能够抢到线程从而一直不被执行,造成任务的“假死”状况。

除了上述几种问题外,还有协作对象间的死锁以及开发调用的问题。这个描述起来会比较困难,也不容易看出死锁来。

避免和解决死锁

通常发生死锁后程序难以自恢复。但也不是不能避免的。 有一些技巧和原则是可以降低死锁可能性的。

最简单的原则是尽可能的减少锁的范围。锁的范围越小,那么竞争的可能性也越小。 尽快释放锁也有助于避开锁顺序。如果一个线程每次最多只能够获取一个锁,那么就不会产生锁顺序死锁。

 


如何设置最有的线程数量

并行的任务增加资源显然能够提高性能,但是如果是串行的任务,增加资源并不一定能够得到合理的性能提升。 Amdahl定律描述的在一个系统中,增加处理器资源对系统行的提升比率。 假定在一个系统中,F是必须串行化执行的比重,N是处理器资源,那么随着N的增加最多增加的加速比:

理论上,当N趋近于无穷大时,加速比最大值无限趋近于1/F 这意味着如果一个程序的串行化比重为50%,那么并行化后最大加速比为2倍。

加速比除了可以用于加速的比率外,也可以用于衡量CPU资源的利用率。如果每一个CPU的资源利用率为100%,那么CPU的资源每次翻倍时,加速比也应该翻倍。 事实上,在拥有10个处理器的系统中,程序如果有10%是串行化的,那么最多可以加速1/(0.1+(1-0.1)/10)=5.3倍,换句话说CPU的利用率只用5.3/10=53%。而如果处理器增加到100倍,那么加速比为9.2倍,也就是说CPU的利用率只有个9.3%

 

 

假设引入的多线程都用于计算,那么性能一定会有很大的提升么? 其实引入多线程以后也会引入更多的开销。

切换上下文

 

如果可运行的线程数大于CPU的内核数,那么OS会根据一定的调度算法,强行切换正在运行的线程,从而使其它线程能够使用CPU周期。

切换线程会导致上下文切换。线程的调度会导致CPU需要在操作系统和进程间花费更多的时间片段,这样真正执行应用程序的时间就减少了。另外上下文切换也会导致缓存的频繁进出,对于一个刚被切换的线程来说,可能由于高速缓冲中没有数据而变得更慢,从而导致更多的IO开销。

内存同步

 

不同线程间要进行数据同步,synchronized以及volatile提供的可见性都会导致缓存失效。线程栈之间的数据要和主存进行同步,这些同步有一些小小的开销。如果线程间同时要进行数据同步,那么这些同步的线程可能都会受阻。

阻塞

 

当发生锁竞争时,失败的线程会导致阻塞。通常阻塞的线程可能在JVM内部进行自旋等待,或者被操作系统挂起。自旋等待可能会导致更多的CPU切片浪费,而操作系统挂起则会导致更多的上下文切换。

了解了性能的提升的几个方面,也了解性能的开销后,应用程序就要根据实际的场景进行取舍和评估。没有一劳永逸的优化方案,不断的进行小范围改进和调整是提高性能的有效手段。当前一些大的架构调整也会导致较大的性能的提升。

简单的原则是在保证逻辑正确的情况小,找到性能瓶颈,小步改进和优化。

 

 

 

 

 

对于多线程编程,源码的分析一下文章总结的很齐全

 

  1. J.U.C 整体认识
  2. 原子操作 part 1 从AtomicInteger开始
  3. 原子操作 part 2 数组、引用的原子操作
  4. 原子操作 part 3 指令重排序与happens-before法则
  5. 原子操作 part 4 CAS操作
  6. 锁机制 part 1 Lock与ReentrantLock
  7. 锁机制 part 2 AQS
  8. 锁机制 part 3 加锁的原理 (Lock.lock)
  9. 锁机制 part 4 锁释放与条件变量 (Lock.unlock And Condition)
  10. 锁机制 part 5 闭锁 (CountDownLatch)
  11. 锁机制 part 6 CyclicBarrier
  12. 锁机制 part 7 信号量 (Semaphore)
  13. 锁机制 part 8 读写锁 (ReentrantReadWriteLock) (1)
  14. 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)
  15. 锁机制 part 10 锁的一些其它问题
  16. 并发容器 part 1 ConcurrentMap (1)
  17. 并发容器 part 2 ConcurrentMap (2)
  18. 并发容器 part 3 ConcurrentMap (3)
  19. 并发容器 part 4 并发队列与Queue简介
  20. 并发容器 part 5 ConcurrentLinkedQueue
  21. 并发容器 part 6 可阻塞的BlockingQueue (1)
  22. 并发容器 part 7 可阻塞的BlockingQueue (2)
  23. 并发容器 part 8 可阻塞的BlockingQueue (3)
  24. 并发容器 part 9 双向队列集合 Deque
  25. 并发容器 part 10 双向并发阻塞队列 BlockingDeque
  26. 并发容器 part 11 Exchanger
  27. 并发容器 part 12 线程安全的List/Set CopyOnWriteArrayList/CopyOnWriteArraySet
  28. 线程池 part 1 简介
  29. 线程池 part 2 Executor 以及Executors
  30. 线程池 part 3 Executor 生命周期
  31. 线程池 part 4 线程池任务拒绝策略
  32. 线程池 part 5 周期性任务调度
  33. 线程池 part 6 线程池的实现及原理 (1)
  34. 线程池 part 7 线程池的实现及原理 (2)
  35. 线程池 part 8 线程池的实现及原理 (3)
  36. 线程池 part 9 并发操作异常体系
  37. 并发总结 part 1 死锁与活跃度
  38. 并发总结 part 2 常见的并发场景
  39. 并发总结 part 3 常见的并发陷阱
  40. 并发总结 part 4  性能与伸缩性

 

 

 

分享到:
评论

相关推荐

    java线程池ThreadPoolExecutor类使用详解.docx

    在《阿里巴巴java开发手册》中...另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

    线程池之ThreadPoolExecutor.docx

    线程池的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,那么超出数量的线程排队等候,等其他线程执行完毕再从队列中取出任务来执行。...

    11-线程池ThreadPoolExecutor底层原理源码分析(上)-周瑜.pdf

    11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...

    12-线程池ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf

    11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...

    Java ThreadPoolExecutor 线程池的使用介绍

    提供工厂方法来创建不同类型的线程池,这篇文章主要介绍了Java ThreadPoolExecutor 线程池的使用介绍,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来...

    Java线程池与ThreadPoolExecutor.pdf

    ——学习参考资料:仅用于个人学习使用! 本代码仅作学习交流,切勿用于商业用途,否则后果自负。若涉及侵权,请联系,会尽快处理! 未进行详尽测试,请自行调试!

    解决python ThreadPoolExecutor 线程池中的异常捕获问题

    这里主要想介绍 python concurrent.futuresthread.ThreadPoolExecutor 线程池中的 worker 引发异常的时候,并不会直接向上抛起异常,而是需要主线程通过调用concurrent.futures.Future.exception(timeout=None) 方法...

    高并发之——通过源码深度解析ThreadPoolExecutor类是如何保证线程池正确运行的

    AtomicInteger类型的常量ctl是贯穿线程池整个生命周期的重要属性,它是一个原子类对象,主要用来保存线程的数量和线程池的状态,我们看下与这个属性相关的代码如下所示。 //主要用来保存线程数量和线程池的状态,高...

    spring线程池ThreadPoolExecutor配置以及FutureTask的使用

    最代码,http://www.zuidaima.com/share/1724478138158080.htm 的代码及例子

    Android线程池管理的代码例子

    Android线程池管理的代码例子。用于演示普通线程池ThreadPoolExecutor、定时器线程池ScheduledExecutorService等功能。

    Java线程池ThreadPoolExecutor原理及使用实例

    主要介绍了Java线程池ThreadPoolExecutor原理及使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    牛客的代码leetcode代码区别-offer:代码练习

    牛客的代码leetcode代码区别 offer ...ThreadPoolExecutor: extendsTest: 继承练习 Guava:Guava学习 NIODemo:NIO练习 Redis: Redis学习记录 redispractice: 练习 SwordOffer: 剑指offer题目练习

    jtp-JohanThreadPool新版

    JohanThreadPool最早起源于jdk-1.4时代,当时如果有完善的ThreadPoolExecutor,或许JohanThreadPool就不会诞生了:) 不过经过多年对JohanThreadPool的小修小改,经过各种使用场合的锤炼,发现不论是开发,还是性能...

    阿里巴巴编码规范 基础技能认证 考题分析(考题+答案).docx

    (线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式) 多选 3.下列哪些说法符合《阿里巴巴Java开发手册》:ACD A .对于“明确停止使用的代码和配置”,如方法、变量、类、配置文件、动态...

    PyQt5多线程防卡死和多窗口用法的实现

    心得:写着写着找到了自己的感觉,还是需要大量的代码和项目来加深对代码的理解 一、PyQt5多线程防卡死 在界面中,通常用会有一些按钮,点击后触发事件,比如去下载一个文件或者做一些操作,这些操作会耗时,如果不...

    线程池并发测试例子

    java代码 ThreadPoolExecutor线程池并发测试例子如有误欢迎指正

    Java开发手册(阿里巴巴带-alibaba-IDE插件)

    《阿里巴巴Java开发手册》是阿里内部Java工程师所遵循的开发规范,涵盖编程规约...这是阿里回馈给Java社区的一份礼物,希望能够帮助企业开发团队在Java开发上更高效、容错、有协作性,提高代码质量,降低项目维护成本。

    在Android线程池里运行代码任务实例

    本节展示如何在线程池里执行任务。流程是,添加一个任务到线程池的工作队列,当有线程可用时(执行完其他任务,空闲,或者还没执行任务),... 代码如下: public class PhotoManager {  public void handleState(Pho

    java线程池概念.txt

    线程安全,并发的知识有加深认知;当然,现在用过的东西并不是代表以后还能娴熟的使用,做好笔记非常重要; 1:必须明白为什么要使用...MyExtendThreadPoolExecutor 继承了 ThreadPoolExecutor,并覆盖了其中的一些...

    Java线程池框架核心代码分析

     下面,我们来分析一下 Java 线程池框架的实现ThreadPoolExecutor。  下面的分析基于JDK1.7  生命周期  ThreadPoolExecutor中,使用CAPACITY的高3位来表示运行状态,分别是:  RUNNING:接收新任务,并且...

Global site tag (gtag.js) - Google Analytics