ThreadPool、ThreadPoolExecutor的原理及用法

ThreadPool接口

public interface ThreadPool<Job extends Runnable> {
	 // 执行一个 Job,这个 Job 需要实现 Runnable
	 void execute(Job job);
	 // 关闭线程池
	 void shutdown();
	 // 增加工作者线程
	 void addWorkers(int num);
	 // 减少工作者线程
	 void removeWorker(int num);
	 // 得到正在等待执行的任务数量
	 int getJobSize();
}

客户端可以通过 execute(Job)方法将 Job 提交入线程池执行,而客户端自身不用等待
Job 的执行完成。除了 execute(Job)方法以外,线程池接口提供了增大/减少工作者线程以
及关闭线程池的方法。这里工作者线程代表着一个重复执行 Job 的线程,而每个由客户
端提交的 Job 都将进入到一个工作队列中等待工作者线程的处理。接下来是线程池接口
的默认实现

ThreadPool实现类

public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
	 // 线程池最大限制数
	 private static final int MAX_WORKER_NUMBERS = 10;
	 // 线程池默认的数量
	 private static final int DEFAULT_WORKER_NUMBERS = 5;
	 // 线程池最小的数量
	 private static final int MIN_WORKER_NUMBERS = 1;
	 // 这是一个工作列表,将会向里面插入工作
	 private final LinkedList<Job> jobs = new LinkedList<Job>();
	 // 工作者列表
	 private final List<Worker> workers = Collections.synchronizedList(new 
		ArrayList<Worker>());
	 // 工作者线程的数量
	 private int workerNum = DEFAULT_WORKER_NUMBERS;
	 // 线程编号生成
	 private AtomicLong threadNum = new AtomicLong();
	 public DefaultThreadPool() {
	 	initializeWokers(DEFAULT_WORKER_NUMBERS);
	 }
	 public DefaultThreadPool(int num) {
		 workerNum = num > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : Math.max(num, 
		MIN_WORKER_NUMBERS);
		 initializeWokers(workerNum);
	 }
	 public void execute(Job job) {
		 if (job != null) {
			 // 添加一个工作,然后进行通知
			 synchronized (jobs) {
				jobs.addLast(job);
				jobs.notify();
			 }
		 }
	 }
	 public void shutdown() {
		 for (Worker worker : workers) {
		 	worker.shutdown();
		 }
	 }
	 public void addWorkers(int num) {
		 synchronized (jobs) {
			 // 限制新增的 Worker 数量不能超过最大值
			 if (num + this.workerNum > MAX_WORKER_NUMBERS) {
			 	num = MAX_WORKER_NUMBERS - this.workerNum;
		 	}
			 initializeWokers(num);
			 this.workerNum += num;
		 }
	 }
	 public void removeWorker(int num) {
		 synchronized (jobs) {
			 if (num >= this.workerNum) {
			 	throw new IllegalArgumentException("beyond workNum");
			 }
			 // 按照给定的数量停止 Worker
			 int count = 0;
			 while (count < num) {
				 Worker worker = workers.get(count);
				 if (workers.remove(worker)) {
					worker.shutdown();
					count++;
			 	}
	 		}
	 	this.workerNum -= count;
	 	}
	 }
	 public int getJobSize() {
		 return jobs.size();
	 }
	 // 初始化线程工作者
	 private void initializeWokers(int num) {
		 for (int i = 0; i < num; i++) {
			 Worker worker = new Worker();
			 workers.add(worker);
			 Thread thread = new Thread(worker, "ThreadPool-Worker-" + threadNum.
			 incrementAndGet());
			 thread.start();
		 }
	 }
	 // 工作者,负责消费任务
	 class Worker implements Runnable {
		 // 是否工作
		 private volatile boolean running = true;
		 public void run() {
			 while (running) {
				 Job job = null;
				 synchronized (jobs) {
					 // 如果工作者列表是空的,那么就 wait
					while (jobs.isEmpty()) {
					 try {
					 	jobs.wait();
					 } catch (InterruptedException ex) {
						 // 感知到外部对 WorkerThread 的中断操作,返回
						 Thread.currentThread().interrupt();
						 return;
					 	}
					 }
					// 取出一个 Job
					job = jobs.removeFirst();
				}
					if (job != null) {
						 try {
						 	job.run();
						 } catch (Exception ex) {
						 	// 忽略 Job 执行中的 Exception
						 }
					 }
			 }
		 }
		 public void shutdown() {
		 	running = false;
		 }
	 }
}

从线程池的实现可以看到,当客户端调用 execute(Job)方法时,会不断地向任务列表
jobs 中添加 Job,而每个工作者线程会不断地从 jobs 上取出一个 Job 进行执行,当 jobs
为空时,工作者线程进入等待状态。
添加一个 Job 后,对工作队列 jobs 调用了其 notify()方法,而不是 notifyAll()方法,
因为能够确定有工作者线程被唤醒,这时使用 notify()方法将会比 notifyAll()方法获得更
小的开销(避免将等待队列中的线程全部移动到阻塞队列中)。
可以看到,线程池的本质就是使用了一个线程安全的工作队列连接工作者线程和客
户端线程,客户端线程将任务放入工作队列后便返回,而工作者线程则不断地从工作队
列上取出工作并执行。当工作队列为空时,所有的工作者线程均等待在工作队列上,当
有客户端提交了一个任务之后会通知任意一个工作者线程,随着大量的任务被提交,更
多的工作者线程会被唤醒。

线程池工作原理

在这里插入图片描述
ThreadPoolExecutor 执行 execute()方法的示意图,如图 :
在这里插入图片描述

ThreadPoolExecutor创建线程

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, 
milliseconds,runnableTaskQueue, handler);

runnableTaskQueue(阻塞队列):

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO 排序元素,吞吐量通常要高于 ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
  4. PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

RejectedExecutionHandler(饱和策略):

  1. AbortPolicy:直接抛出异常。(常用)
  2. CallerRunsPolicy:只用调用者所在线程来运行任务。
  3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  4. DiscardPolicy:不处理,丢弃掉。

向线程池提交任务

分别为 execute()和 submit()方法。

  1. execute()方法用于提交不需要返回值的任务:
threadPool.execute(new Runnable() {
	 @Override
		public void run() {
		
		 }
});
  1. submit()方法用于提交需要返回值的任务:
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<?> haha = executor.submit(new Callable<Object>() {
    @Override
    public Object call() throws Exception {
        return "haha";
    }
});
Object o = haha.get();
System.out.println(o);
executor.shutdown();

通过 future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候可能任务没有执行完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子Link

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值