线程池

线程池原理和流程

  • 线程等待池,即线程队列BlockingQueue
  • 任务处理池,PoolWorker,即正在工作的Thread列表(HashSet<Worker>)

public class ThreadPoolTest{
	public static void main(String[] args)throws InterruptedException{
		/**
			创建线程池:
				初始化线程数2
				核心线程数4
				最大线程数6
				任务队列最多允许1000个任务
		*/
	  	final ThreadPool threadPool = new BasicThreadPool(2,6,4,1000);
	  	
	  	//定义20个任务并且提交给线程
	  	for(int i=0;i<20;i++){
			threadPool.execute(() -> {
				try{
					TimeUnit.SECONDS.sleep(10);
				  	System.out.println(Thread.currentThread().getName() + "is running done");
				}catch(){}
			});
		}
	  
	  	for(;;){
			System.out.println(threadPool.getActioveount());
		  	System.out.println(threadPool.getQueueSize());
		  	System.out.println(threadPool.getCoreSize());
		    System.out.println(threadPool.getMaxSize());
		  	TimeUnit.SECONDS.sleep(5);
		}
	}
}

==================================================

Executors工具类创建线程池

  • newSingleThreadExecutor创建一个单线程的线程池
public static void main(String[] args){
	//该线程池只有一个线程在工作,相当于单线程串行执行所有任务
  	ExecutorService executor = Executors.newSingleThreadExecutor();
  	for(int i=0;i<10;i++){
		final int no = i;
	  	Runnable runnable = new Runnable(){
			public void run(){
				try{
					System.out.println("into"+no);
				  	Thread.sleep(1000L);
				  	System.out.println("end"+no);
				}catch(){
				
				}
			}
		};
	  	executor.execute(runnable);
	}
  	executor.shutdown();
  	System.out.println("Thread Main End");
}
  • newCachedThreadPool创建一个缓存池大小可根据需要伸缩的线程池
ExecutorService executor = Executors.newCachedThreadPool();
  • newFixedThreadPool创建一个可重用固定线程数的线程池,以共享的无界队列方式运行这些线程
//创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);

《阿里巴巴Java开发手册》中,明确禁止使用Executors创建线程池,并要求开发者直接使用ThreadPoolExectorScheduledThreadPoolExecutor进行创建,强制开发者明确线程池的运行策略,使其对线程池的每个配置参数皆做到心中有数,以规避因使用不当而造成资源耗尽的风险

ThreadPoolExecutor创建线程池

/**
	corePoolSize,线程池的基本大小。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程。当需要执行的任务数大于线程池基本大小的时候就不再创建。如果调用线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
	
	maximumPoolSize,线程池最大的大小。如果队列满了并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务(无界任务队列,该参数无效果)
	
	keepAliveTime,线程活动保持时间,线程池的工作线程空闲后,保持存活的时间,如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程利用率
	
	milliseconds,线程活动保持的时间单位,可选的有DAYS天,HOURS小时,MINUTES分钟…………
	
	runnableTaskQueue,任务队列。用于保存等待执行的任务的阻塞队列。
		ArrayBlockingQueue数组结构的有界阻塞队列,先进先出
		LinkedBlockingQueue链表结构的阻塞队列,先进先出。静态工厂Executors.newFixedThreadPool()使用该队列
		SynchronousQueue不存储元素的阻塞队列,每插入一个元素必须等另一个线程调用移除操作Executors.newCachedThreadPool使用这个队列
		PrioritBlockingQueue具有优先级无限阻塞队列
		
	handler,饱和策略。当队列和线程池都满了,必须采取策略处理提交的新任务,默认AbortPolicy,表示无法处理新任务时抛出异常。
		AbortPolicy 直接抛出异常
		CallerRunsPolicy 只用调用者所在线程来运行任务
		DiscardOldesPolicy 丢弃队列里最近的一个任务,并执行当前任务
		DiscardPolicy 不处理,丢弃掉
		自定义策略,实现RejectedExecutionHandler接口(如记录日志,持久化不能处理的任务)
*/
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,
					   milliseconds,runnableTaskQueue,handler);


/**
	向线程池提交任务
*/
threadsPool.execute(new Runnable(){ //无返回值
	@Override
  	public void run(){
	
	}
});

Future<Object> future = executor.submit(); //有返回值
try{
	Obejct s = future.get();
}catch(){}


/**
	线程池关闭:
	原理,逐个遍历调用线程的interrupt方法来中断线程,如果无法响应中断的任务可能永远复发终止。
	shutdownNow:首先将线程池的状态设置为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。(任务不一定要执行完)
	shutdown:只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程
	
	只要调用了以上任意一个,isShutdown方法返回true
	
*/

ScheduleThreadPoolExecutor按时间调度来执行任务

  • 延迟执行任务
public ScheduleFuture<?> shedule(Runnable command,long delay,TimeUnit unit);

public <V>ScheduleFuture<V> schedule(Callable<V>callable,long delay,TimeUnit unit);
  • 周期执行任务
/**
	按固定频率执行,与任务本身执行时间无关
	前提条件:任务执行时间必须小于间隔时间,例如间隔时间是5s,每5s执行一次任务,任务的执行时间必须小于5s
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

/**
	按固定间隔执行,与任务本身执行时间有关。
	比如:任务本身执行时间10s,间隔2s,则下一次开始执行的时间就是12s
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

Future接口和实现Future接口的FutureTask,代表异步算的

Runnable接口或Callable接口的实现类提交(submitThreadPoolExecutorScheduledThreadPoolExecutorThreadPoolExecutorScheduledThreadPoolExecutor会返回一个FutureTask象。

FutureTask可以交Executor行,也可以由线程直接行(FutureTask.run())根据FutureTask.run()方法被行的机,FutureTask可以于下面3种状

  • 未启动(执行FutureTask.get()方法将线程阻塞)
  • 已启动(执行FutureTask.get()方法将线程阻塞)
  • 已完成(FutureTask.get()方法将线程立即返回果或抛出异常)

线程池注意:

  • 线程池一定要在合理的单例模式下才有效,不可以放在services方法中创建线程池,因为每当这个方法被调用的时候要创建一大堆线程池。
  • 线程池数量不要设置过大,请求过载发挥不了线程池的优点了
  • 注意死锁问题

实际应用:

Tomcat中线程池配置

Nginx线程池

数据库连接池(阿里巴巴的Druid,提供qiang)

分布式系统中实现高并发

全部评论

相关推荐

2 9 评论
分享
牛客网
牛客企业服务