自定义线程池
自定义线程池
概要
我们知道线程是一个重量级的资源,创建、启动以及销毁线程都是比较耗费系统资源的。因此对线程的重复利用一种是非常好的程序设计习惯,加之系统中可创建的线程数量是有限的,线程数量和系统性能是一种抛物线的关系,也就是说当线程数量达到某个数值的时侯,性能反倒会降低很多,因此对线程的管理,尤其是数量的控制更能直接决定程序的性能。
一、什么是线程池?
顾名思义,线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。
二、 为什么要用线程池
池化技术想必大家已经屡见不鲜了,线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
三、线程池原理
所谓线程池,通俗的理解就是有一个池子,里面存放着已经创建好的线程,当有任务提交给线程池执行时,池子中的某个线程会主动执行该任务。如果池子中的线程数量不够应付数量众多的任务时,则需要自动扩充新的线程到池子中,但是该数量是有限的,就好比池塘的水界线一样。当任务比较少的时候,池子中的线程能够自动回收,释放资源。为了能够异步地提交任务和缓存未被处理的任务,需要有一个任务队列。
如下图所示:
通过上面的描述可知,一个完整的线程池应该具备如下要素。
1)任务队列:用于缓存提交的任务。
2)线程数量管理功能:一个线程池必须能够很好地管理和控制线程数量,可通过如下三个参数来实现,比如创建线程池时初始的线程数量init;线程池自动扩充时最大的线程数量max;在线程池空闲时需要释放线程但是也要维护一定数量的活跃数量或者核心数量core。有了这三个参数,就能够很好地控制线程池中的线程数量,将其维护在一个合理的范围之内,三者之间的关系是init<=core<=max。
3)任务拒绝策略:如果线程数量已达到上限且任务队列已满,则需要有相应的拒绝策略来通知任务提交者。
4)线程工厂:主要用于个性化定制线程,比如将线程设置为守护线程以及设置线程名称等。
5)QueueSize: 任务队列主要存放提交的Runnable,但是为了防止内存溢出,需要有 limit 数量对其进行控制。
6)KeepAlive时间:该时间主要决定线程各个重要参数自动维护的时间间隔。
7)Internal Task 任务类
InternalTask 是Runnable的一个实现,主要用于线程池内部,该类会使用到 RunnableQueue,然后不断地从queue中取出某个Runnable,并运行Runnable的run 方法,除此之外,代码还对该类增加了一个开关方法stop,主要用于停止当前线程,一般在线程池销毁和线程数量维护的时候会使用到。
四、线程池的应用
自JDK1.5起,util包提供了ExecutorService线程池的实现,主要目的是为了重复利用线程,提高系统效率。本文从原理入手,设计一个线程池,目的是理解线程池的关键功能和注意事项。
下面将写一个简单的程序分别测试线程池的任务提交、线程池线程数量的动态扩展,以及线程池的挡毁功能。
先整体梳理下:
1)ThreadPool接口
2)RunnableQueue接口
3)ThreadFactory接口
4)DenyPolicy接口和RunnableDenyException自定义异常类
5)InternalTask类
6)LinkedRunnableQueue实现类
7)BasicThreadPool 线程池实现类
BasicThreadPool实现类分析
BasicThreadPool实现类_属性和构造器的编写
BasicThreadPool实现类_初始化线程池和提交任务
BasicThreadPool实现类_线程池自动维护
BasicThreadPool实现类_线程池销毁和其他方法
线程池中线程数量的维护主要由run负责,这也是为什么BasicThreadPool继承自Thread了,不过不推荐使用直接继承的方式。
下面是代码示例:
1. ThreadPool 线程池接口:
1 /** 2 * 线程池接口 3 */ 4 5 public interface ThreadPool { 6 7 /** 8 * 提交任务到线程池 9 * 10 * @param runnable 11 */ 12 void execute(Runnable runnable); 13 14 /** 15 * 关闭线程池 16 */ 17 void shutdown(); 18 19 /** 20 * 查看线程池是否已经被shutdown 21 * 22 * @return 23 */ 24 boolean isShutdown(); 25 26 /** 27 * 获取线程池的初始化大小 28 * 29 * @return 30 */ 31 int getInitSize(); 32 33 /** 34 * 获取线程池最大的线程数 35 * 36 * @return 37 */ 38 int getMaxSize(); 39 40 /** 41 * 获取线程池的核心线程数量 42 * 43 * @return 44 */ 45 int getCoreSize(); 46 47 /** 48 * 获取线程池用于缓存任务队列的大小 49 * 50 * @return 51 */ 52 int getQueueSize(); 53 54 /** 55 * 获取线程池中活跃线程的数量 56 * 57 * @return 58 */ 59 int getActiveCount(); 60 }
2. RunnableQueue 任务队列:
1 /** 2 * 任务队列,主要用于缓存提交到线程池中的任务 3 */ 4 5 public interface RunnableQueue { 6 7 /** 8 * 当有新的任务进来时首先会offer到队列中 9 */ 10 void offer(Runnable runnable); 11 12 13 /** 14 * 工作线程通过take方法获取Runnable 15 * @return 16 */ 17 Runnable take(); 18 19 /** 20 * 获取任务队列中任务的数量 21 * @return 22 */ 23 int size(); 24 }
3. ThreadFactory 线程工厂
1 @FunctionalInterface 2 public interface ThreadFactory { 5 Thread createThread(Runnable runnable); 8 }
4. DenyPolicy 拒绝策略
1 /** 2 * 拒绝策略 3 */ 4 @FunctionalInterface 5 public interface DenyPolicy { 6 void reject(Runnable runnable, ThreadPool threadPool); 7 8 /** 9 * 该拒绝策略会直接将任务丢弃 10 */ 11 class DiscardDenyPolicy implements DenyPolicy { 12 @Override 13 public void reject(Runnable runnable, ThreadPool threadPool) { 14 // ... 15 System.out.println(runnable + "任务已被丢弃"); 16 } 17 } 18 19 /** 20 * 该拒绝策略会向任务提交者抛出异常 21 */ 22 class AbortDenyPolicy implements DenyPolicy { 23 @Override 24 public void reject(Runnable runnable, ThreadPool threadPool) { 25 throw new RunnableDenyException("任务" + runnable + " 将被终止。"); 26 } 27 } 28 29 30 /** 31 * 该拒绝策略会使任务在提交者所在的线程中执行任务 32 */ 33 class RunnerDenyPolicy implements DenyPolicy { 34 @Override 35 public void reject(Runnable runnable, ThreadPool threadPool) { 36 if (!threadPool.isShutdown()) { 37 runnable.run(); 38 } 39 } 40 } 41 }
自定义拒绝策略异常类:
1 /** 2 * 自定义拒绝策略异常类 3 */ 4 5 public class RunnableDenyException extends RuntimeException { 6 public RunnableDenyException(String message) { 7 super(message); 8 } 9 }
5. InternalTask 任务类
1 public class InternalTask implements Runnable { 2 3 private final RunnableQueue runnableQueue; 4 private volatile boolean running = true; 5 6 public InternalTask(RunnableQueue runnableQueue) { 7 this.runnableQueue = runnableQueue; 8 9 } 10 11 @Override 12 public void run() { 13 Runnable task; 14 15 // 如果当前任务为running并且没有被中断,则不断的从 queue 中获取runnable然后执行run方法 16 while (running && !Thread.currentThread().isInterrupted()) { 17 task = runnableQueue.take(); 18 task.run(); 19 } 20 } 21 22 /** 23 * 停止当前任务,主要会在线程池的 shutdown 方法中使用 24 */ 25 public void stop() { 26 this.running = false; 27 } 28 }
6. LinkedRunnableQueue 任务队列实现类
1 public class LinkedRunnableQueue implements RunnableQueue { 2 3 /** 4 * 任务队列的最大容量 5 */ 6 private final int limit; 7 8 /** 9 * 若任务队列中的任务已经满了,则需要执行拒绝策略 10 */ 11 private final DenyPolicy denyPolicy; 12 13 /** 14 * 存放任务的队列 15 */ 16 private final LinkedList<Runnable> runnableList = new LinkedList<>(); 17 private final ThreadPool threadPool; 18 19 public LinkedRunnableQueue(int limit, DenyPolicy denyPolicy, ThreadPool threadPool) { 20 this.limit = limit; 21 this.denyPolicy = denyPolicy; 22 this.threadPool = threadPool; 23 } 24 25 /** 26 * 当有新的任务进来时首先会offer到队列中 27 */ 28 @Override 29 public void offer(Runnable runnable) { 30 synchronized (runnableList) { 31 if (runnableList.size() >= limit) { 32 //无法容纳新的任务时执行拒绝策略 33 denyPolicy.reject(runnable, threadPool); 34 } else { 35 // 将任务加入到队尾,并且唤醒阻塞中的线程 36 runnableList.addLast(runnable); 37 runnableList.notifyAll(); 38 } 39 } 40 } 41 42 /** 43 * 工作线程通过take方法获取Runnable 44 * @return 45 */ 46 @Override 47 public Runnable take() { 48 synchronized (runnableList) { 49 while (runnableList.isEmpty()){ 50 try { 51 // 如果任务队列中没有可执行的任务,则当前线程将会挂起 52 runnableList.wait(); 53 } catch (InterruptedException e) { 54 e.printStackTrace(); 55 } 56 } 57 58 // 从任务队列头部移除一个任务 59 return runnableList.removeFirst(); 60 } 61 } 62 63 /** 64 * 获取任务队列中任务的数量 65 * @return 66 */ 67 @Override 68 public int size() { 69 synchronized (runnableList) { 70 //返回当前任务队列中的任务数 71 return runnableList.size(); 72 } 73 } 74 }
7. BasicThreadPool 线程池实现类
1 public class BasicThreadPool extends Thread implements ThreadPool { 2 /** 3 * 初始化线程数量 4 */ 5 private final int initSize; 6 /** 7 * 线程池最大线程数量 8 */ 9 private final int maxSize; 10 /** 11 * 线程池核心线程数量 12 */ 13 private final int coreSize; 14 /** 15 * 当前活跃的线程数量 16 */ 17 private int activeCount; 18 19 /** 20 * 线程池是否被 shutdown 的标记 21 */ 22 private volatile boolean isShutdown = false; 23 /** 24 * 线程存活时间 25 */ 26 private final long keepAliveTime; 27 /** 28 * 时间单位 29 */ 30 private final TimeUnit timeUnit; 31 /** 32 * 创建线程所需的工厂 33 */ 34 private final ThreadFactory threadFactory; 35 private final static ThreadFactory DEFAULT_THREAD_FACTORY = new DefaultThreadFactory(); 36 37 38 /** 39 * 工厂实现类(内部类) 40 */ 41 private static class DefaultThreadFactory implements ThreadFactory { 42 43 private static final AtomicInteger GROUP_COUNTER = new AtomicInteger(0); 44 private static final ThreadGroup GROUP = new ThreadGroup("MyThreadGroupPool-" + GROUP_COUNTER.getAndIncrement()); 45 private static final AtomicInteger COUNTER = new AtomicInteger(0); 46 47 @Override 48 public Thread createThread(Runnable runnable) { 49 return new Thread(GROUP, runnable, "thread-pool-" + COUNTER.getAndIncrement()); 50 } 51 } 52 53 /** 54 * 任务队列 55 */ 56 private final RunnableQueue runnableQueue; 57 58 /** 59 * 工作线程队列 60 */ 61 private final Queue<ThreadTask> threadQueue = new ArrayDeque<>(); 62 63 64 private static class ThreadTask { 65 Thread thread; 66 InternalTask internalTask; 67 68 public ThreadTask(Thread thread, InternalTask internalTask) { 69 this.thread = thread; 70 this.internalTask = internalTask; 71 } 72 } 73 74 /** 75 * 拒绝策略 76 */ 77 private final static DenyPolicy DEFAULT_DENY_POLICY = new DenyPolicy.DiscardDenyPolicy(); 78 79 /** 80 * 构造器 81 * 82 * @param initSize 83 * @param maxSize 84 * @param coreSize 85 * @param queueSize 86 */ 87 public BasicThreadPool(int initSize, int maxSize, int coreSize, int queueSize) { 88 this(initSize, maxSize, coreSize, queueSize, DEFAULT_DENY_POLICY, 10, TimeUnit.SECONDS, DEFAULT_THREAD_FACTORY); 89 } 90 91 public BasicThreadPool(int initSize, int maxSize, int coreSize, int queueSize, DenyPolicy denyPolicy, long keepAliveTime, TimeUnit timeUnit, ThreadFactory threadFactory) { 92 this.initSize = initSize; 93 this.maxSize = maxSize; 94 this.coreSize = coreSize; 95 this.runnableQueue = new LinkedRunnableQueue(queueSize, denyPolicy, this); 96 this.keepAliveTime = keepAliveTime; 97 this.timeUnit = timeUnit; 98 this.threadFactory = threadFactory; 99 // 初始化线程池 100 init(); 101 } 102 103 /** 104 * 初始化线程池 105 */ 106 private void init() { 107 108 //启动线程池 109 this.start(); 110 111 //创建任务线程 112 System.out.println("初始化线程池数量>>>" + initSize); 113 for (int i = 0; i < initSize; i++) { 114 newThread(); 115 } 116 } 117 118 /** 119 * 创建任务线程并启动 120 */ 121 private void newThread() { 122 InternalTask internalTask = new InternalTask(runnableQueue); 123 Thread thread = this.threadFactory.createThread(internalTask); 124 125 //线程和任务类关联在一起 126 ThreadTask threadTask = new ThreadTask(thread, internalTask); 127 threadQueue.offer(threadTask); 128 this.activeCount++; 129 130 //启动任务线程 131 thread.start(); 132 } 133 134 /** 135 * 从线程池中移除某个线程 136 */ 137 private void removeThread() { 138 ThreadTask threadTask = threadQueue.remove(); 139 threadTask.internalTask.stop(); 140 this.activeCount--; 141 } 142 143 /** 144 * 提交任务到线程池 145 * @param runnable 146 */ 147 @Override 148 public void execute(Runnable runnable) { 149 if (this.isShutdown) { 150 throw new IllegalStateException("线程池已销毁。"); 151 } 152 153 //提交任务 154 this.runnableQueue.offer(runnable); 155 } 156 157 /** 158 * 线程池自动维护(相当于线程池的扩容) 159 */ 160 @Override 161 public void run() { 162 163 //线程池没有被shutdown也没有中断的标识 164 while (!isShutdown && !isInterrupted()) { 165 try { 166 timeUnit.sleep(keepAliveTime); 167 } catch (InterruptedException e) { 168 e.printStackTrace(); 169 } 170 171 synchronized (this) { 172 if (isShutdown) { 173 break; 174 } 175 176 // 当前的队列中有任务尚未处理,并且activeCount < coreSize则继续扩容 177 System.out.println("activeCount>>>" + activeCount); 178 if (runnableQueue.size() > 0 && activeCount < coreSize) { 179 System.out.println("线程池扩容至coreSize>>>" + coreSize); 180 for (int i = initSize; i < coreSize; i++) { 181 newThread(); 182 } 183 184 continue; 185 } 186 187 // 当前的队列中有任务尚未处理,并且activeCount < maxSize则继续扩容 188 if (runnableQueue.size() > 0 && activeCount < maxSize) { 189 System.out.println("线程池扩容至maxSize>>>" + maxSize); 190 for (int i = coreSize; i < maxSize; i++) { 191 newThread(); 192 } 193 } 194 195 //当前的队列中没有任务,则需要回收,回收至coreSize即可 196 if (runnableQueue.size() == 0 && activeCount > coreSize) { 197 System.out.println("当前队列中没有任务,回收线程至>>>" + coreSize); 198 for (int i = coreSize; i < activeCount; i++) { 199 removeThread(); 200 } 201 } 202 } 203 } 204 } 205 206 207 /** 208 * 线程池销毁 209 */ 210 @Override 211 public void shutdown() { 212 synchronized (this) { 213 if (isShutdown) { 214 return; 215 } 216 217 isShutdown = true; 218 threadQueue.forEach(threadTask -> { 219 threadTask.internalTask.stop(); 220 221 //打上中断标记 222 threadTask.thread.interrupt(); 223 }); 224 225 this.interrupt(); 226 } 227 } 228 229 @Override 230 public boolean isShutdown() { 231 if (this.isShutdown) { 232 throw new IllegalStateException("线程池已销毁。"); 233 } 234 return this.isShutdown; 235 } 236 237 @Override 238 public int getInitSize() { 239 if (this.isShutdown) { 240 throw new IllegalStateException("线程池已销毁。"); 241 } 242 return this.initSize; 243 } 244 245 @Override 246 public int getMaxSize() { 247 if (this.isShutdown) { 248 throw new IllegalStateException("线程池已销毁。"); 249 } 250 return this.maxSize; 251 } 252 253 /** 254 * 获取线程池的核心线程数量 255 * 256 * @return 257 */ 258 @Override 259 public int getCoreSize() { 260 if (this.isShutdown) { 261 throw new IllegalStateException("线程池已销毁。"); 262 } 263 return this.coreSize; 264 } 265 266 /** 267 * 获取线程池用于缓存任务队列的大小 268 * 269 * @return 270 */ 271 @Override 272 public int getQueueSize() { 273 if (this.isShutdown) { 274 throw new IllegalStateException("线程池已销毁。"); 275 } 276 return this.runnableQueue.size(); 277 } 278 279 /** 280 * 获取线程池中活跃线程的数量 281 * 282 * @return 283 */ 284 @Override 285 public int getActiveCount() { 286 if (this.isShutdown) { 287 throw new IllegalStateException("线程池已销毁。"); 288 } 289 return this.activeCount; 290 } 291 }
测试代码:
1 public class ThreadPoolTest { 2 3 public static void main(String[] args) throws InterruptedException { 4 5 //====1. 构造器中初始化线程池 2.启动线程池(线程池自动维护) 3.execute提交任务 ====// 6 7 // 定义线程池,初始化线程数为2,最大线程数为6,核心线程数为4,任务队列最多允许 1000 个任务 8 final ThreadPool threadPool = new BasicThreadPool(2, 6, 4, 1000); 9 10 // 定义20个任务并且提交给线程池 11 for (int i = 0; i < 20; i++) { 12 threadPool.execute(() -> { 13 //具体的业务逻辑 14 try { 15 TimeUnit.SECONDS.sleep(10); 16 System.out.println(Thread.currentThread().getName() + " 正在运行。"); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 }); 21 } 22 23 //不断输出线程池的信息 24 while (true) { 25 System.out.println("threadPool.getActiveCount() = " + threadPool.getActiveCount()); 26 System.out.println("threadPool.getQueuesize() = " + threadPool.getQueueSize()); 27 System.out.println("threadPool.getCoresize() = " + threadPool.getCoreSize()); 28 System.out.println("threadPool.getMaxsize() = " + threadPool.getMaxSize()); 29 System.out.println("--------------------------------------------"); 30 TimeUnit.SECONDS.sleep(5); 31 } 32 } 33 } 34 35 //运行结果 36 初始化线程池数量>>>2 37 thread-pool-2 正在运行。 38 thread-pool-5 正在运行。 39 activeCount>>>5 40 当前队列中没有任务,回收线程至>>>4 41 threadPool.getActiveCount() = 4 42 threadPool.getQueuesize() = 0 43 threadPool.getCoresize() = 4 44 threadPool.getMaxsize() = 6
分析:
1)初始化构造函数中包含了:属性赋值 以及 初始化线程池init ,其中init方法中做了两件事情:启动线程池 以及 创建任务线程并启动
2)客户端测试的时候,使用execute提交任务到线程池
3)newThread方法中启动任务线程,实际执行了InteralTask 任务类的run方法,该方法会一直检查:如果当前任务为running并且没有被中断,则不断的从 queue 中获取runnable然后执行run方法