Java并发编程(三)Java线程池

目录

一、线程池概念

二、线程池状态

三、Excecutors四种创建线程池方法

四、Java中的ThreadPoolExecutor类

五、线程执行流程

六、一个简单的线程池实现


一、线程池概念

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

二、线程池状态

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

三、Excecutors四种创建线程池方法

1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

四、Java中的ThreadPoolExecutor类

ThreadPoolExecutor的构造函数:

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

参数介绍:

  • corePoolSize:核心池大小,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  • maximumPoolSize:线程池最大线程数,超过这个数后的线程将被阻塞;
  • keepAliveTime:线程存活时间,如果线程数大于corePoolSize,则多出来的线程会先没有任务后指定时间关闭默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用;
  • unit:参数keepAliveTime的时间单位;
  • workQueue:阻塞队列,用来存储等待执行的任务;
  • threadFactory:线程工厂,主要用来创建线程;
  • handler:表示当拒绝处理任务时的策略。

其中,workQueue类型为BlockingQueue<Runnable>,BlockingQueue类型可以为:

  • ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
  • LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小;
  • synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

其中,handler任务拒绝策略包括:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常;
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务;
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

其中,corePoolSize、maximumPoolSize、workQueue关系:

  • 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程;
  • 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行;
  • 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务;
  • 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理;
  • 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程;
  • 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭。

线程池执行流程:

五、线程执行流程

1、如果线程池当前线程数量少于corePoolSize,则addWorker(command, true)创建新worker线程,如创建成功返回,如没创建成功,则执行后续步骤;addWorker失败的原因可能是:

  • 线程池已经shutdown,shutdown的线程池不再接收新任务。
  • workerCountOf(c) < corePoolSize 判断后,由于并发,别的线程先创建了worker线程。

2、如果线程池还在running状态,将task加入workQueue阻塞队列中,如果加入成功,进行double-check,如果加入失败(可能是队列已满),则执行后续步骤;double-check主要目的是判断刚加入workQueue阻塞队列的task是否能被执行。

  • 如果线程池已经不是running状态了,应该拒绝添加新任务,从workQueue中删除任务。
  • 如果线程池是运行状态,或者从workQueue中删除任务失败(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务。

3、如果线程池不是running状态 或者 无法入队列,尝试开启新线程,扩容至maxPoolSize,如果addWork(command, false)失败了,拒绝当前command。

六、一个简单的线程池实现

// 通用线程池接口
public interface ThreadPool<Job extends Runnable> {
	/**
	 * 任务执行
	 * @param job
	 */
	void excecute(Job job);

	/**
	 * 关闭线程池
	 */
	void shutdown();

	/**
	 * 增加工作线程
	 * @param num
	 */
	void addWorkers(int num);

	/**
	 * 去掉工作线程
	 * @param num
	 */
	void removeWorkers(int num);

	/**
	 * 获得任务数
	 * @return
	 */
	int getJobSize();
}
// 线程池的一种实现
public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {

	private static final int DEFAULT_SIZE = 10;
	private final LinkedList<Job> jobs = new LinkedList<>();
	private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
	private AtomicLong threadNum = new AtomicLong();


	public void excecute(Job job) {
		synchronized(jobs) {
			jobs.addLast(job);
			job.notify();
		}
	}

	public void shutdown() {
		for(Worker worker : workers) {
			worker.shutdown();
		}
	}

	public void addWorkers(int num) {
		synchronized(jobs) {
			// 此处需要数量校验
			for(int i=0;i<num;i++) {
				Worker worker = new Worker();
				Thread t = new Thread(worker);
				threadNum.incrementAndGet();
				t.start();
			}
		}
	}

	public void removeWorkers(int num) {
		synchronized(jobs) {
			// 此处需要数量校验
			for(int i=0;i<num;i++) {
				Worker worker = workers.get(i);
				if(workers.remove(worker)){
					worker.shutdown();
				}
			}
		}
	}

	@Override
	public int getJobSize() {
		return jobs.size();
	}

	class Worker implements Runnable {
		private volatile boolean running = true;
		public void run() {
			while(running) {
				Job job = null;
				synchronized(jobs) {
					while(jobs.isEmpty()) {
						try{
							jobs.wait();
						} catch (Exception e) {

						}
					}
					job = jobs.removeFirst();
				}
				if(job != null) {
					job.run();
				}
			}
		}

		public void shutdown() {
			running = false;
		}
	}

}

 

参考文章:

https://www.cnblogs.com/zhujiabin/p/5404771.html

http://www.cnblogs.com/dolphin0520/p/3932921.html

https://www.cnblogs.com/trust-freedom/p/6681948.html

猜你喜欢

转载自blog.csdn.net/ss1300460973/article/details/84634977