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

通过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类中几种创建各类型线程池方法及简单实例

    JDK自带线程池分析

    下面是使用 Executors 工厂类创建线程池的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedThreadPool { public static void main...

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

    在Java中,使用`ExecutorService`和`ThreadPoolExecutor`来创建线程池,而不是直接使用`Executors`,因为`Executors`创建的线程池可能会导致资源耗尽的问题。 集合是Java中存储数据的主要工具,包括List、Set和Map...

    线程池 Executors

    Java中的线程池Executors java中的线程池Executors是Java并发编程中的一种重要概念,它提供了一种高效、灵活的线程管理机制。使用线程池可以降低资源消耗,提高响应速度,提高线程的可管理性。 线程池的优点 1. ...

    26_多线程_第1天(Thread、线程创建、线程池)_讲义

    在Java编程语言中,多线程是并发执行任务的关键机制,极大地提高了程序的效率和响应速度。...通过Thread类、线程创建方法以及线程池的使用,开发者能够更好地管理和调度系统资源,以实现高效的并发处理。

    线程池详解:线程池七大核心参数、线程池工作原理、线程池的创建方式、线程池的拒绝策略、如何合理分配线程池大小

    Java中使用ExecutorService接口和Executors类来创建线程池。常见创建方式有: 1. newFixedThreadPool:固定大小的线程池,核心线程数与最大线程数相同。 2. newSingleThreadExecutor:单线程线程池,保证所有任务按...

    java 线程池管理类:Executors_.docx

    `java.util.concurrent.Executors` 是Java并发编程中一个非常重要的工具类,主要用于创建不同类型的线程池对象。通过使用`Executors`类,开发者可以方便地管理和控制线程池的行为,从而提高系统的性能和资源利用率。...

    线程池的实现以及底层原理.docx

    1. 避免使用Executors工具类创建线程池,应使用ThreadPoolExecutor构造方法创建线程池。 2. 需要根据实际情况指定合适的线程池参数,例如核心线程数、最大线程数、阻塞队列等。 3. 需要对线程池进行监控和维护,例如...

    详细分析JAVA 线程池

    Executors工厂类提供了多个静态工厂方法来创建线程池,包括: * newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。 * newFixedThreadPool(int nThreads...

    Java并发编程中使用Executors类创建和管理线程的

    使用`Executors`创建线程池时,需要注意以下几点: - **线程池大小的选择**:线程池的大小应根据系统的硬件资源和任务性质进行合理设置,过大可能导致内存资源浪费,过小可能使CPU资源未充分利用。 - **拒绝策略**...

    Java中的线程与线程池.pptx

    使用Executors构建线程池时需要注意,如使用默认设置可能导致线程池积压过多任务或创建过多线程,从而引发内存溢出。因此,推荐自定义配置线程池,如使用有界队列ArrayBlockingQueue,并合理设定核心线程数、最大...

    JAVA使用线程池查询大批量数据

    除了`ThreadPoolExecutor`,Java还提供了`Executors`工具类,它提供了一些预设的线程池配置,如`newFixedThreadPool`(固定大小线程池)、`newSingleThreadExecutor`(单线程线程池)等,方便开发者快速创建线程池。...

    线程池管理线程demo

    Java中,我们通常使用`Executors`类提供的静态工厂方法来创建线程池,如`newFixedThreadPool(int nThreads)`创建固定大小的线程池,`newSingleThreadExecutor()`创建只有一个线程的线程池,`newCachedThreadPool()`...

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

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

    java线程池的源码分析.zip

    线程池通过维护一个工作线程集合来管理任务的执行,避免了频繁创建和销毁线程的开销,提高了系统的效率和响应速度。 1. **线程池的工作流程**:线程池接收到新任务后,会根据当前运行状态和队列情况决定如何处理。...

    java 线程池源代码

    例如,`Executors`工厂类提供了四种静态方法来快速创建线程池: 1. `newFixedThreadPool(int nThreads)`: 创建固定大小的线程池,适用于需要限制并发执行任务数量的情况。 2. `newSingleThreadExecutor()`: 创建...

    线程池的实现(JAVA)

    3. **Executors**: 这是一个工厂类,提供了多种创建线程池的静态方法。例如,`newFixedThreadPool(int n)` 创建固定大小的线程池,`newSingleThreadExecutor()` 创建只有一个线程的线程池,`newCachedThreadPool()` ...

    Executor,Executors,ExecutorService比较.docx

    【Executor、Executors和ExecutorService详解】...在实际编程中,我们通常通过`Executors`创建线程池,然后利用`ExecutorService`的特性来执行和管理任务。了解这些概念和用法,有助于我们构建高效、稳定的多线程环境。

    Java中多线程的使用线程池.docx

    4. **创建线程池**:使用`Executors`类的静态方法创建线程池,例如 `newFixedThreadPool(int nThreads)` 创建一个固定大小的线程池,其中线程数量是可预设的。 5. **使用线程池**: - **创建线程池对象**:如 `...

    关于线程池的代码demo

    在Java中,我们可以使用`Executors`工厂类创建线程池,如`newFixedThreadPool`用于创建固定大小的线程池,`newSingleThreadExecutor`创建只有一个线程的线程池,`newCachedThreadPool`创建可缓存的线程池,等等。...

Global site tag (gtag.js) - Google Analytics