深圳高端网站设计建设,广东省建设监理协会网站 首页,怎么把网站放到服务器上,慈溪 网站建设JAVA线程池原理详解一
一. 线程池的优点
线程是稀缺资源#xff0c;使用线程池可以减少创建和销毁线程的次数#xff0c;每个工作线程都可以重复使用。可以根据系统的承受能力#xff0c;调整线程池中工作线程的数量#xff0c;防止因为消耗过多内存导致服务器崩溃。
二…JAVA线程池原理详解一
一. 线程池的优点
线程是稀缺资源使用线程池可以减少创建和销毁线程的次数每个工作线程都可以重复使用。可以根据系统的承受能力调整线程池中工作线程的数量防止因为消耗过多内存导致服务器崩溃。
二. 线程池的创建
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,RejectedExecutionHandler handler) corePoolSize线程池核心线程数 maxinumPoolSize线程池最大线程数 keepAliveTime当活跃线程数大于核心线程数时空闲的多余线程最大存活时间 unit存活时间的单位 workQueue存放线程任务的队列 handler超出线程范围和队列容量的任务的处理程序处理策略
三. 线程池的实现原理
提交一个任务到线程池中线程池的处理流程如下
判断线程池里的核心线程是否都在执行任务如果不是核心线程空闲或者还有核心线程没有被创建则创建一个新的工作线程来执行任务。如果核心线程都在执行任务则进入下个流程。线程池判断工作队列是否已满如果工作队列没有满则将新提交的任务存储在这个工作队列里。如果工作队列满了则进入下个流程。判断线程池里的线程是否都处于工作状态如果没有则创建一个新的工作线程来执行任务。如果已经满了则交给饱和策略来处理这个任务。 四. 线程池的源码解读
ThreadPoolExecutor的execute()方法
public void execute(Runnable command) {if (command null)throw new NullPointerException(); //如果线程数大于等于基本线程数或者线程创建失败将任务加入队列if (poolSize corePoolSize || !addIfUnderCorePoolSize(command)) { //线程池处于运行状态并且加入队列成功if (runState RUNNING workQueue.offer(command)) {if (runState ! RUNNING || poolSize 0)ensureQueuedTaskHandled(command);} //线程池不处于运行状态或者加入队列失败则创建线程创建的是非核心线程else if (!addIfUnderMaximumPoolSize(command)) //创建线程失败则采取阻塞处理的方式reject(command); // is shutdown or saturated}}创建线程的方法addIfUnderCorePoolSize(command)
private boolean addIfUnderCorePoolSize(Runnable firstTask) {Thread t null;final ReentrantLock mainLock this.mainLock;mainLock.lock();try {if (poolSize corePoolSize runState RUNNING)t addThread(firstTask);} finally {mainLock.unlock();}if (t null)return false;t.start();return true;}我们重点来看addThread()方法
private Thread addThread(Runnable firstTask) {Worker w new Worker(firstTask);Thread t threadFactory.newThread(w);if (t ! null) {w.thread t;workers.add(w);int nt poolSize;if (nt largestPoolSize)largestPoolSize nt;}return t;}这里将线程封装成工作线程worker并放入工作线程组里worker类的方法run方法
public void run() {try {Runnable task firstTask;firstTask null;while (task ! null || (task getTask()) ! null) {runTask(task);task null;}} finally {workerDone(this);}}五. 我们通过一个程序来观察线程池的工作原理
44 创建一个线程
创建一个线程
public class ThreadPoolTest implements Runnable
{Overridepublic void run(){try{Thread.sleep(300);}catch (InterruptedException e){e.printStackTrace();}}
}线程池循环运行16个线程
public static void main(String[] args){LinkedBlockingQueueRunnable queue new LinkedBlockingQueueRunnable(5);ThreadPoolExecutor threadPool new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, queue);for (int i 0; i 16 ; i){threadPool.execute(new Thread(new ThreadPoolTest(), Thread.concat(i )));System.out.println(线程池中活跃的线程数 threadPool.getPoolSize());if (queue.size() 0){System.out.println(----------------队列中阻塞的线程数 queue.size());}}threadPool.shutdown();}执行结果
线程池中活跃的线程数 1
线程池中活跃的线程数 2
线程池中活跃的线程数 3
线程池中活跃的线程数 4
线程池中活跃的线程数 5
线程池中活跃的线程数 5
----------------队列中阻塞的线程数1
线程池中活跃的线程数 5
----------------队列中阻塞的线程数2
线程池中活跃的线程数 5
----------------队列中阻塞的线程数3
线程池中活跃的线程数 5
----------------队列中阻塞的线程数4
线程池中活跃的线程数 5
----------------队列中阻塞的线程数5
线程池中活跃的线程数 6
----------------队列中阻塞的线程数5
线程池中活跃的线程数 7
----------------队列中阻塞的线程数5
线程池中活跃的线程数 8
----------------队列中阻塞的线程数5
线程池中活跃的线程数 9
----------------队列中阻塞的线程数5
线程池中活跃的线程数 10
----------------队列中阻塞的线程数5
Exception in thread main java.util.concurrent.RejectedExecutionException: Task Thread[Thread15,5,main] rejected from java.util.concurrent.ThreadPoolExecutor232204a1[Running, pool size 10, active threads 10, queued tasks 5, completed tasks 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)at test.ThreadTest.main(ThreadTest.java:17)从结果可以观察出
创建的线程池具体配置为核心线程数量为5个全部线程数量为10个工作队列的长度为5。我们通过queue.size()的方法来获取工作队列中的任务数。运行原理
刚开始都是在创建新的线程达到核心线程数量5个后新的任务进来后不再创建新的线程而是将任务加入工作队列任务队列到达上线5个后新的任务又会创建新的普通线程直到达到线程池最大的线程数量10个后面的任务则根据配置的饱和策略来处理。我们这里没有具体配置使用的是默认的配置AbortPolicy:直接抛出异常。
当然为了达到我需要的效果上述线程处理的任务都是利用休眠导致线程没有释放
六. RejectedExecutionHandler饱和策略
当队列和线程池都满了说明线程池处于饱和状态那么必须对新提交的任务采用一种特殊的策略来进行处理。这个策略默认配置是AbortPolicy表示无法处理新的任务而抛出异常。JAVA提供了4中策略
AbortPolicy直接抛出异常CallerRunsPolicy只用调用所在的线程运行任务DiscardOldestPolicy丢弃队列里最近的一个任务并执行当前任务。DiscardPolicy不处理丢弃掉。
我们现在用第四种策略来处理上面的程序
public static void main(String[] args){LinkedBlockingQueueRunnable queue new LinkedBlockingQueueRunnable(3);RejectedExecutionHandler handler new ThreadPoolExecutor.DiscardPolicy();ThreadPoolExecutor threadPool new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue,handler);for (int i 0; i 9 ; i){threadPool.execute(new Thread(new ThreadPoolTest(), Thread.concat(i )));System.out.println(线程池中活跃的线程数 threadPool.getPoolSize());if (queue.size() 0){System.out.println(----------------队列中阻塞的线程数 queue.size());}}threadPool.shutdown();}执行结果
线程池中活跃的线程数 1
线程池中活跃的线程数 2
线程池中活跃的线程数 2
----------------队列中阻塞的线程数1
线程池中活跃的线程数 2
----------------队列中阻塞的线程数2
线程池中活跃的线程数 2
----------------队列中阻塞的线程数3
线程池中活跃的线程数 3
----------------队列中阻塞的线程数3
线程池中活跃的线程数 4
----------------队列中阻塞的线程数3
线程池中活跃的线程数 5
----------------队列中阻塞的线程数3
线程池中活跃的线程数 5
----------------队列中阻塞的线程数3这里采用了丢弃策略后就没有再抛出异常而是直接丢弃。在某些重要的场景下可以采用记录日志或者存储到数据库中而不应该直接丢弃。
设置策略有两种方式
方法一
RejectedExecutionHandler handler new ThreadPoolExecutor.DiscardPolicy();ThreadPoolExecutor threadPool new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue,handler);方法二
ThreadPoolExecutor threadPool new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, queue);threadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());