`
victorzhzh
  • 浏览: 202087 次
  • 来自: ...
社区版块
存档分类
最新评论

通过Executors创建创建的线程池分析I

阅读更多

对于线程池主要关心的内容有:线程池的大小控制、池中的队列控制、池饱和时的策略,下面我们从这三个角度来分析一下Executors生成的不同池。

1、ThreadPoolExecutor构造函数介绍:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

 第一个corePoolSize:JDK文档里定义为核心线程数,很耐人寻味的一个词,但是很重要,我们在下面的分析中经常会使用它,对于它的理解,我们最后在取总结;

第二个maximumPoolSize:线程池中可容纳的最大线程个数;

第三个keepAliveTime:当当前的线程数大于核心线程数时, 多余的空闲线程等待新任务的最长时间,如果没有新任务进入则多余的空闲线程将被销毁;

第四个unit:第三个参数的单位;

第五个workQueue:保持任务的队列(任务队列基本有三种形式:无限队列、有限队列和同步移交);

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

相对第一个构造函数多了一个handler,这个参数用来指定线程池饱和时的策略(饱和策略主要有:中止、遗弃、遗弃最旧的、调用者运行);还有两个构造函数这里就不再列出了,不同点就是可以指定threadFactory。

 

2、newCachedThreadPool:

有了上面的知识,我们来分析一下newCachedThreadPool构造的线程池,先看一下方法内容:

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

这里指定了,核心线程数为0,线程池中最大的线程数为0x7fffffff,当前线程个数大于核心线程数时,多余线程的存活时间为60秒,任务队列指定的使用的是SynchronousQueue,饱和策略使用的是AbortPolicy(中止策略)

下面我们来看一下newCachedThreadPool创建的线程池:线程池大小、池中的队列、池饱和时的策略

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }

 上面的代码是线程池执行任务的核心代码,我们分析一下,

poolSize记录的是当前线程池中的线程个数,corePoolSize是我们指定的线程池的核心线程个数,在newCachedThreadPool创建的线程池中poolSize >= corePoolSize永远都会成立,因为corePoolSize被我们设置成0,当前线程池的大小永远是大于等于0的,接下来看一下内层代码,先判断一下当前线程池的状态是否在运行状态,然后调用workQueue.offer(command)方法,在newCachedThreadPool创建的线程池中workQueue是SynchronousQueue,因此调用的是SynchronousQueue的如下代码:

public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        return transferer.transfer(e, true, 0) != null;
}

这里的transferer是TransferStack,由于我们timed和nanos(transfer方法的第二和第三个参数)设置为true和0,因此会永远执行transfer方法的如下代码:

if (timed && nanos <= 0) {      // can't wait
      if (h != null && h.isCancelled())
             casHead(h, h.next);     // pop cancelled node
       else
            return null;
} 

而这里transferer.transfer(e, true, 0)方法的返回是null,因此workQueue.offer(command)方法永远是false。

这样机只会执行:

else if (!addIfUnderMaximumPoolSize(command))
           reject(command);

 这个分支,addIfUnderMaximumPoolSize方法如下:

private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
        Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < maximumPoolSize && runState == RUNNING)
                t = addThread(firstTask);
        } finally {
            mainLock.unlock();
        }
        if (t == null)
            return false;
        t.start();
        return true;
}

 这里又会判断poolSize当前线程池中线程个数是否小于maximumPoolSize线程池中可容纳的最大个数,如果小于则创建一个线程来执行对应的任务返回true,如果不小于则会返回false,当返回false时,回去执行reject(command)方法,会根据中止的策略来处理新提交的任务。

这里要说明一下SynchronousQueue:

SynchronousQueue并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到SynchronousQueue中时必须有另一个线程正在等待接受移交的任务,如果没有这样的线程存在,只要当前池的大小还小于最大值,线程池就会创建一个新的线程来执行这个任务,因此使用SynchronousQueue作为线程池队列的前提是,要么池的大小是无限的或者可以接受被拒绝策略。

例子:

public class CachedThreadPoolTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		((ThreadPoolExecutor)service).setMaximumPoolSize(3);
		((ThreadPoolExecutor)service).setCorePoolSize(2);
		((ThreadPoolExecutor)service).setKeepAliveTime(20, TimeUnit.SECONDS);
		service.submit(new SleepTask());
		service.submit(new SleepTask());
		service.submit(new SleepTask());
		try{
			service.submit(new SleepTask());--将被拒绝
		}catch(RejectedExecutionException e){
			System.out.println("Rejected1");
		}
		
		try{
			service.submit(new SleepTask());--将被拒绝
		}catch(RejectedExecutionException e){
			System.out.println("Rejected2");
		}
	}

}

 结果:

Rejected1
Rejected2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3

SleepTask代码

public class SleepTask implements Callable<String> {

	@Override
	public String call() throws Exception {
		TimeUnit.SECONDS.sleep(10);
		System.out.println(Thread.currentThread().getName());
		return Thread.currentThread().getName();
	}

}
 

 

分享到:
评论

相关推荐

    Java中Executors类中几种创建各类型线程池

    Java中Executors类中几种创建各类型线程池方法及简单实例

    线程-线程池-锁-集合-Map-队列.docx

    线程是系统中可执行调度的...线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,规避资源耗尽的风险。集合的详细描述,以及集合中的异同点,HashMap不同jdk版本区别,ConcurrentHashMap介绍。

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

    而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,...

    Executor,Executors,ExecutorService比较.docx

    Executors一共可以创建下面这四类线程池: 1.newFixedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。`` 2.newFixedThreadPool 创建一个定长线程池,...

    Java 4种线程池的使用

    Java通过Executors提供四种线程池,分别为:  newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。  newFixedThreadPool 创建一个定长线程池...

    验证阿里巴巴禁用Executors的原因

    【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下: 1) ...

    Java线程池文档

    Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用简介》[4],《Java5中的线程池实例讲解》[5],《ThreadPoolExecutor使用和思考》[6] ...

    两种线程池写法

    ThreadPoolManager:Executors.newFixedThreadPool(num * 2);// 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程 threadPool: 线程池 创建线程池,销毁线程池,添加新任务

    更好的使用Java线程池

     ExecutorService基于池化的线程来执行用户提交的任务,通常可以简单的通过Executors提供的工厂方法来创建ThreadPoolExecutor实例。  线程池解决的两个问题:1)线程池通过减少每次做任务的时候产生的性能消耗来...

    线程池相关详解及总结.doc

    线程池设计思路,java提供自带的线程池类ThreadPoolExecutor详解,Executors调用静态方法创建线程池

    Java并发编程相关源码集 包括多任务线程,线程池等.rar

    Java并发编程常见知识点源码集锦,涉及到对象锁,Executors多任务线程框架,线程池等示例,列出一些源码包中包括的内容:  volatile关键字的非原子性、volatile关键字的使用、AtomicInteger原子性操作、线程安全小...

    java socket线程池

     //创建线程池   //Runtime的availableProcessors()方法返回当前系统的CPU的数目 //系统的CPU越多,线程池中工作线程的数目也越多 executorService= Executors.newFixedThreadPool(   Runtime.getRuntime()....

    面试官这样问Java 线程池,我的回答让他竖起了大拇指!

    前言 这周我投递出了简历,岗位是java后端开发工程师。...Excutors创建线程池便捷方法如下: Executors.newFixedThreadPool(100);//创建固定大小的线程池 Executors.newSingleThreadExecutor();//创

    google用于管理线程,符合阿里线程池规范ThreadFactoryBuilder的jar包 guava 27.0.1-jre.jar

    最近阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 其中ThreadFactoryBuilder...

    Java中的Executors类最全讲义

    2.1 创建线程池 2.2 提交任务 2.3 关闭线程池 线程池的分类 3.1 FixedThreadPool 3.2 CachedThreadPool 3.3 ScheduledThreadPool 3.4 SingleThreadExecutor 3.5 WorkStealingPool 线程池的参数配置 4.1 核心线程数 ...

    node-threadpool:使用工作线程的节点线程池

    创建工作线程通常很昂贵,线程池维护线程并允许您即时提交工作,而不必支付重新创建线程的费用。 使用节点的新worker_thread API,池中的线程可以相互传递消息,以及对共享内存进行读写。 用法 完整的API文档可以在...

    第7章-JUC多线程v1.1.pdf

    JDK中提供了一个线程池工厂: Executors ,很多工厂方法, 可以创建多种线程池 1.单一线程池 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 该线程池只有一个线程, 若多个任务...

    《〈Java开发手册(泰山版)〉灵魂13问》.zip

    为什么阿里巴巴禁止使用Executors创建线程池?31 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法?37 为什么阿里巴巴不建议在for循环中使用“+”进行字符串拼接?44 为什么阿里巴巴禁止在foreach循环里进行...

    cachedThreadPoolExample:该项目是通过使用缓存线程池进行并行处理的示例

    该项目是通过使用缓存线程池进行并行处理的示例。 假设我们将关键字列表传递给 Web 服务,该服务根据该关键字返回数据。 如果我们有几千个关键字,那么遍历它们将是一个昂贵且漫长的过程。 同时加载所有这些也不是一...

    面试灵魂13问.zip

    为什么阿里巴巴禁止使用 Executors 创建线程池? 31 为什么阿里巴巴要求谨慎使用 ArrayList 中的 subList 方法? 37 为什么阿里巴巴不建议在 for 循环中使用“+”进行字符串拼接? 44 为什么阿里巴巴禁止在 ...

Global site tag (gtag.js) - Google Analytics