java中的线程池

Java中的线程池

【1】使用线程池的好处:

	1)降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
	2)提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行。
	3)提高线程的可管理性。
		说明:不同的业务,我们一般会使用不同的线程池,以便于对同一业务的线程进行统一管理:(日志)监控、线程池参数调优。


【2】构造方法:

	public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);

	参数说明:
	
		1)corePoolSize:核心池的大小。
		
			1)在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务。
			2)默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务。
				注意:即使其它空闲的线程能够执行新任务也会去创建线程,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
			3)调用prestartAllCoreThreads()或prestartCoreThread()方法来预创建线程,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。
			
			
		2)workQueue:任务缓存队列,是一个阻塞队列,用来存储等待执行的任务;类型为BlockingQueue<Runnable>,通常可以取下面4种类型:
		
			1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
			2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQueue。
				eg:静态工厂方法Executors.newFixedThreadPool()就是使用的这个队列。
			3)SynchronousQueue:这个队列不会保存提交的任务,而是直接新建一个线程来执行新来的任务。
				说明:每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。
				eg:静态工厂方法Executors.newCachedThreadPool使用的就是这个队列。
			4)PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
			
			说明:
				1>一般使用LinkedBlockingQueue和SynchronousQueue
				2>建议使用有界队列,有界队列能增加系统的稳定性。
					eg:如果线程池里的工作线程全部阻塞,任务积压在线程池里,如果设置成无界队列,那么这个队列会越来越大,有可能会撑满内存,导致整个系统不可用。

			
		3)maximumPoolSize:线程池中允许创建的最大线程数。
		
			1)如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程来执行任务。
			2)如果使用了无界的任务队列,则这个参数就不起什么作用了(队列默认的大小是:Integer.MAX_VALUE,在队列未满之前,线程池是不会再去创建新线程了)。
		
		
		4)RejectedExecutionHandler:任务拒绝策略,当任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
		
			1)ThreadPoolExecutor.AbortPolicy:		丢弃任务并抛出RejectedExecutionException异常,默认使用该策略。
			2)ThreadPoolExecutor.DiscardPolicy:	丢弃任务,但是不抛出异常;会导致被丢弃的任务无法再次被执行
			3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程);会导致被丢弃的任务无法再次被执行
			4)ThreadPoolExecutor.CallerRunsPolicy:	由调用线程处理该任务;主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 

			说明:
				1>我们也可以实现RejectedExecutionHandler接口来自定义任务拒绝策略。
				2>我们也可以在ThreadPoolExecutor外面包装一层,在wrapper中自定义更多的场景:
					eg:在执行自定义的execute方法时,当抛出RejectedExecutionException异常时,我们可以根据传入的参数(一般通过注解的方式来传参)来决定采取什么策略。
				3>RejectedExecutionHandler的设计属于设计模式中的策略模式。
	
		5)keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。
		
			默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。
			但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
			
			
		6)TimeUnit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
		
			TimeUnit.DAYS;				天
			TimeUnit.HOURS;				小时
			TimeUnit.MINUTES;			分钟
			TimeUnit.SECONDS;			秒
			TimeUnit.MILLISECONDS;		毫秒
			TimeUnit.MICROSECONDS;		微妙
			TimeUnit.NANOSECONDS;		纳秒

			
		7)ThreadFactory:创建线程的工厂
			说明:
				1>我们可以自定义线程工厂,以便给每个线程设置更有意义的名字。
				2>可以通过修改java.util.concurrent.Executors的内部类DefaultThreadFactory来自定义线程工厂。



			
【3】其它方法:

	任务的提交:
		execute(Runnable command):	提交后没有返回值,故无法判断任务是否被线程池执行成功。
			
		submit():	提交后返回一个Future对象,通过这个future对象可以判断任务是否执行成功。
			通过future的get()方法来获取返回值,该方法会阻塞当前线程直到任务完成。
			get(long timeout, TimeUnit unit)方法:阻塞当前线程一段时间后立即返回,这时候任务可能没有执行完。
	
	

	线程池容量的动态调整:
		setCorePoolSize()		设置核心池的大小
		setMaximumPoolSize()	设置线程池最大能创建的线程数

	线程池的关闭:
	
		原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
		
		shutdown()		
			1>将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
			2>不再接受新的任务,但是不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止。
		shutdownNow()	
			1>首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并且清空任务缓存队列,并返回等待执行任务的列表.

		说明:
			1)只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。
			2)当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。
	

【4】线程池的状态:

	// RUNNING状态:线程池正常运行,可以接受新的任务并处理队列中的任务
    private static final int RUNNING    = -1 << COUNT_BITS;
	// SHUTDOWN状态:不再接受新任务,但是会执行队列中的任务
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
	// STOP状态:不再接受新任务,不处理队列中的任务,中断正在处理的任务
    private static final int STOP       =  1 << COUNT_BITS;
	// 过渡状态:所有的任务都执行完了,线程池已经没有有效的线程了,此时线程池的状态为过渡状态,并且将要调用terminated()方法
    private static final int TIDYING    =  2 << COUNT_BITS;
	// 终止状态:terminated()方法调用完成后的状态
    private static final int TERMINATED =  3 << COUNT_BITS;

	1>当线程池刚创建后,线程池处于RUNNING状态,可以接受新的任务并处理队列中的任务
	2>如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
	3>如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
	4>当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

	
【5】线程池的处理流程:

	ThreadPoolExecutor执行execute方法分下面4种情况。
		1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
		2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
		3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理该任务。
		4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用	RejectedExecutionHandler.rejectedExecution()方法。


	源码(jdk8):
		public void execute(Runnable command) {
			if (command == null)
				throw new NullPointerException();
			/*
			 * Proceed in 3 steps:
			 *
			 * 1. If fewer than corePoolSize threads are running, try to
			 * start a new thread with the given command as its first
			 * task.  The call to addWorker atomically checks runState and
			 * workerCount, and so prevents false alarms that would add
			 * threads when it shouldn't, by returning false.
			 *
			 * 2. If a task can be successfully queued, then we still need
			 * to double-check whether we should have added a thread
			 * (because existing ones died since last checking) or that
			 * the pool shut down since entry into this method. So we
			 * recheck state and if necessary roll back the enqueuing if
			 * stopped, or start a new thread if there are none.
			 *
			 * 3. If we cannot queue task, then we try to add a new
			 * thread.  If it fails, we know we are shut down or saturated
			 * and so reject the task.
			 */
			int c = ctl.get();
			if (workerCountOf(c) < corePoolSize) {			// 1)线程数小于corePoolSize,创建线程并立即执行任务。
				if (addWorker(command, true))
					return;
				c = ctl.get();
			}
			if (isRunning(c) && workQueue.offer(command)) { // 2)线程数达到corePoolSize,任务被添加到队列中。
				int recheck = ctl.get();
				if (! isRunning(recheck) && remove(command))
					reject(command);
				else if (workerCountOf(recheck) == 0)
					addWorker(null, false);
			}
			else if (!addWorker(command, false)) 			// 3)队列已满,则创建新线程并立即执行该任务。
				reject(command);							// 4)线程数已达到maximumPoolSize,创建线程失败,执行拒绝策略。
		}
	
		
		
		

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值