线程池

通过一个简单的例子说明线程池的基本原理与 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue的差异。

package com.larry.learning.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 测试线程池主要参数:corePoolSize maximunPoolSize 以及使用不同队列执行任务的效果
 * 测试场景:
 * 1、执行任务数不超过corePoolSize
 * 2、执行任务数超过corePoolSize,并且任务个数不超过corePoolSize + queue.size()
 * 3、执行任务数超过corePoolSize + queue.size(),但小于等于maximunPoolSize
 * 4、执行任务数超过maximunPoolSize,但小于等于maximunPoolSize + queue.size()
 * 5、执行任务数超过maximunPoolSize + queue.size()
 * @author Administrator
 *
 */
public class ThreadPoolTest {
	
	public static void main(String[] args) {
		//线程池核心池线程数
		
		int corePoolSize = Integer.valueOf(args[0]);
		//线程池最大线程数
		int maximumPoolSize = Integer.valueOf(args[1]);
		
		/**
		 * 线程池队列类型:
		 * 1 ArrayBlockingQueue
		 * 2 LinkedBlockingQueue
		 * 3 SynchronousQueue
		 */
		int queueType = Integer.valueOf(args[2]);
		
		//执行的总任务数
		int totalWork = Integer.valueOf(args[3]);
		
		long keepAliveTime = 60L;
		TimeUnit unit = TimeUnit.SECONDS;
		
		BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(10);
		
		switch (queueType) {
		case 1:
			workQueue = WorkQueueHelper.ARRAY_QUEUE.getWorkQueue();
			break;
		case 2:
			workQueue = WorkQueueHelper.LINKED_QUEUE.getWorkQueue();
			break;
		case 3:
			workQueue = WorkQueueHelper.SYNC_QUEUE.getWorkQueue();
			break;
		}
		
		System.out.println("corePoolSize: " + corePoolSize + ", maximumPoolSize: " + maximumPoolSize + ", totalWork: " + totalWork);
		System.out.println("running " + workQueue.getClass().getName() + "queueCapacity: " + workQueue.remainingCapacity());
		
		ThreadPoolExecutor executors = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new NamedThreadFactory());
		
		for (int i = 0; i < totalWork; i++) {
			executors.execute(new LongTimeWork());
		}
	}
	

}

class LongTimeWork implements Runnable {
	
	@Override
	public void run() {
		System.out.println("thread running: " + Thread.currentThread().getName());
		while (true) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

enum WorkQueueHelper {
	/**
	 * 容量只有1的阻塞队列
	 */
	ARRAY_QUEUE(new ArrayBlockingQueue<Runnable>(1)),
	LINKED_QUEUE(new LinkedBlockingQueue<Runnable>(1)),
	
	SYNC_QUEUE(new SynchronousQueue<Runnable>());
	
	private BlockingQueue<Runnable> workQueue;
	
	WorkQueueHelper(BlockingQueue<Runnable> workQueue) {
		this.workQueue = workQueue;
	}
	
	public BlockingQueue<Runnable> getWorkQueue() {
		return workQueue;
	}
	
	public void setWorkQueue(BlockingQueue<Runnable> workQueue) {
		this.workQueue = workQueue;
	}
	
	public String getQueueType() {
		return this.workQueue.getClass().getName();
	}
}

class NamedThreadFactory implements ThreadFactory {

	private AtomicInteger indexCount = new AtomicInteger(1);
	@Override
	public Thread newThread(Runnable r) {
		Thread t = new Thread(r, "TestThreadPool_thread_" + indexCount.getAndIncrement());
		return t;
	}
	
}

测试场景1:

ArrayBlockingQueue:

LinkedBlockingQueue: 

 

SynchronousQueue: 

结论:

当任务数小于线程池核心线程数时,使用三种阻塞队列,都会创建新的线程来执行任务。

测试场景2:

ArrayBlockingQueue: 

 

LinkedBlockingQueue:  

SynchronousQueue:  

结论:

当任务数等于线程池核心池大小+阻塞队列大小时,创建的任务优先提交到线程池由核心池线程执行,当核心池线程个数达到corePoolSize,使用ArrayBlockingQueue和LinkedBlockingQueue的线程池会将提交的任务存入队列,由于SynchronousQueue不保存任务,因此会继续创建新的线程来执行任务。因此,可以看到使用ArrayBlockingQueue和LinkedBlockingQueue的线程池运行中的线程数是5,而SynchronousQueue的线程池运行中的线程个数是6.

测试场景3:

ArrayBlockingQueue:  

LinkedBlockingQueue:  

SynchronousQueue:   

当提交的任务数大于线程池核心池+队列大小时,可以看到使用ArrayBlockingQueue和LinkedBlockingQueue的线程池运行中的线程个数是7,也就是说,当核心池线程全部在运行中,且队列已经满了,就会继续创建新的线程来执行任务。总计提交8个任务,核心池个数为5,队列大小为1,因此队列中存放一个任务,再创建两个线程执行任务。而SynchronousQueue的线程池运行中线程数是8。

测试场景4:

ArrayBlockingQueue:   

LinkedBlockingQueue:  

SynchronousQueue: 

结论:

当任务数等于线程池最大允许线程数+队列大小,使用三种队列的线程池,都创建10个线程运行任务 ,但使用ArrayBlockingQueue和LinkedBlockingQueue的线程池因为队列容量为1,故正常运行,而使用SynchronousQueue的线程池使用饱和策略拒绝了第11个提交的任务。

测试场景5:

ArrayBlockingQueue: 

LinkedBlockingQueue:   

SynchronousQueue:  

结论:

当任务数超过队列大小+线程池最大线程数,使用三种队列的线程池都使用饱和策略拒绝新提交的任务。

 

综上,可以得出线程池的基本实现原理:

1、提交的任务首先由核心池线程执行;

2、当核心池线程都处于运行状态,对于ArrayBlockingQueue和LinkedBlockingQueue的线程池,会将任务存到队列中

3、当队列也存满时,会继续创建新的线程执行任务

4、当新创建线程后,线程池总线程数达到max上限,再提交任务,触发线程池饱和策略。 

此外,也可以看出ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue的差异:

1、ArrayBlockingQueue和LinkedBlockingQueue可以设置容量大小;

2、SynchronousQueue不能设置容量,对使用SynchronousQueue的线程池,只起到手递手的作用。也就是说,来一个任务就会提交给一个线程执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值