线程池
来源:互联网 发布:分词软件 编辑:程序博客网 时间:2024/05/18 01:39
1.为什么使用线程池
⑴避免创建和销毁线程的系统开销
⑵控制最大并发数,防止阻塞
⑶对线程进行简单管理,如延迟执行
2.在Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,ThreadPoolExecutor提供四个构造函数。
//五个参数的构造函数public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)//六个参数的构造函数-1public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)//六个参数的构造函数-2public 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
新建线程时如果corePoolSize,则新建的是核心线程,默认情况下会一直存在于线程池中,如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中.
⑵int maximumPoolSize
该线程池中线程总数最大值。线程总数 =核心线程数+非核心线程数
⑶long keepAliveTime
非核心线程闲置超时时长,一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉,如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么它也会起作用,直到核心线程为0.
⑷TimeUnit unit
keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:
NANOSECONDS : 1微毫秒= 1微秒/ 1000
MICROSECONDS : 1微秒= 1毫秒/ 1000
MILLISECONDS : 1毫秒= 1秒/1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
⑸BlockingQueue<Runnable> workQueue
该线程池中的任务队列:维护着等待执行的Runnable对象
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务
常用的workQueue类型:
①SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
②LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
③ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
④DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
⑹ThreadFactory threadFactory
创建线程的方式,这是一个接口,你new他的时候需要实现他的Thread newThread(Runnable r)方法,一般用不上。
⑺RejectedExecutionHandler handler
这玩意儿就是抛出异常专用的,不指定也有默认的,一般用不上。
3.ThreadPoolExecutor四个重要方法:
execute() //可以向线程池提交一个任务,交由线程池处理submit() //也是用来向线程池提交任务,和execute()比可以返回任务执行的结果,实际上就是调用execute()方法,利用Future获取结果shutdown() //关闭线程池shutdownNow() //关闭线程池4.向ThreadPoolExecutor添加任务
⑴通过ThreadPoolExecutor.execute(Runnable command)方法即可向线程池内添加一个任务
⑵ThreadPoolExecutor的策略
当一个任务被添加进线程池时:
①线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
②线程数量达到了corePools,则将任务移入队列等待
③队列已满,新建线程(非核心线程)执行任务
④队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天(RejectedExecutionHandler)抛出异常
5.线程池的原理
主要从以下几方面讲解:线程池 状态,任务的执行,线程池中线程初始化,任务缓存队列及排队策略,任务拒绝策略,线程池关闭,线程池容量的动态调整。
⑴线程池状态
AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; //高三位111,线程池会接受新任务,并处理阻塞队列中的任务 private static final int SHUTDOWN = 0 << COUNT_BITS; //高三位000,不会接受新任务,但会处理阻塞队列中的任务 private static final int STOP = 1 << COUNT_BITS; //高三位001,不会接受新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务 private static final int TIDYING = 2 << COUNT_BITS; //高三位010 private static final int TERMINATED = 3 << COUNT_BITS; //高三位011
⑵execute()方法
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); //如果提交了空任务,抛出异常 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { //workerCountOf(c)方法可以获取当前正在运行的线程数 if (addWorker(command, true)) return; //如果当前线程数量少于 corePoolSize,调用 addWorker(command, true)去增加新的核心线程。 c = ctl.get(); } // if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); //如果线程池被关闭,从workQueue移除command else if (workerCountOf(recheck) == 0) addWorker(null, false); //线程池没有被关闭,并且没有 worker 在工作,则调用 addWorker(null, false) 新增线程 } else if (!addWorker(command, false)) //插入队列不成功,且当前线程数量小于最大喜爱那城池数量,创建新线程执行任务,失败抛出异常 reject(command); }⑶addWorker()方法
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); //当前运行线程的数量 int rs = runStateOf(c); //获取当前线程池状态 // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; //如果状态显示线程池已经被关闭,或者正在被关闭,workQueue 已经空了,则直接返回false(创建新的线程失败) for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //如果当前是添加核心线程,线程池数量大于等于 corePoolSize,返回false。 如果当前是添加非核心线程,线程池数量大于等于 maximumPoolSize,则直接返回false if (compareAndIncrementWorkerCount(c)) break retry; //将线程数量加1,如果增加成功,则退出循环,执行后面的代码。失败一般都是因为线程数不同步。 c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) //再检查一遍运行状态,如果运行状态改变了,将从外循环开头开始执行,再次确认能否添加任务。 continue retry; // else CAS failed due to workerCount change; retry inner loop } } //下面的代码功能是创建新的Worker实例 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { final ReentrantLock mainLock = this.mainLock; w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { mainLock.lock(); try { int c = ctl.get(); int rs = runStateOf(c); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { //启动新线程 t.start(); workerStarted = true; } } } finally { if (! workerStarted) //如果启动线程失败 addWorkerFailed(w); } return workerStarted; }⑷shutdown()方法
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); //获取 mainLock 锁 try { checkShutdownAccess(); //判断调用者是否有权限shutdown线程池 advanceRunState(SHUTDOWN); //CAS+循环设置线程池状态为shutdown interruptIdleWorkers(); //中断所有空闲线程 onShutdown(); } finally { mainLock.unlock(); //解锁 } tryTerminate(); //尝试终止线程池 }⑸shutdownNow()方法
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); //中断所有线程,包括正在运行任务的 tasks = drainQueue(); //将workQueue中的元素放入一个List并返回 } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
⑹tryTerminate()方法
final void tryTerminate() { for (;;) { int c = ctl.get(); if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; if (workerCountOf(c) != 0) { // Eligible to terminate interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // //CAS:将线程池的ctl变成TIDYING(所有的任务被终止,workCount为0,为此状态时将会调用terminated()方法),期间ctl有变化就会失败,会再次for循环 if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { terminated(); //需子类实现 } finally { ctl.set(ctlOf(TERMINATED, 0)); //将线程池的ctl变成TERMINATED termination.signalAll(); //唤醒调用了 等待线程池终止的线程 awaitTermination() } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
6.常见的四种线程池
Java通过Executors提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor的参数实现的
⑴CachedThreadPool()
可缓存线程池:
线程数无限制
有空闲线程则复用空闲线程,若无空闲线程则新建线程
一定程序减少频繁创建/销毁线程,减少系统开销
创建方法:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
⑵FixedThreadPool()
定长线程池:
可控制线程最大并发数(同时执行的线程数)
超出的线程会在队列中等待
//nThreads => 最大线程数即maximumPoolSizeExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);//threadFactory => 创建线程的方法,这就是我叫你别理他的那个星期六!你还看!ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);源码:public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
⑶ScheduledThreadPool()
定时线程池:
支持定时及周期性任务执行。
创建方法:
//nThreads => 最大线程数即maximumPoolSizeExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);源码:public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize);}//ScheduledThreadPoolExecutor():public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());}
⑷SingleThreadExecutor()
单线程化的线程池:
有且仅有一个工作线程执行任务
所有任务按照指定顺序执行,即遵循队列的入队出队规则
创建方法:
单线程化的线程池:有且仅有一个工作线程执行任务所有任务按照指定顺序执行,即遵循队列的入队出队规则创建方法:ExecutorService singleThreadPool = Executors.newSingleThreadPool();源码:public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}
还有一个Executors.newSingleThreadScheduledExecutor()结合了3和4,就不介绍了,基本不用。
- 线程与线程池
- 线程池 线程优先级
- Executor线程,线程池
- 线程、多线程、线程池
- IOS-线程、线程池
- Java线程:线程池
- 线程和线程池
- 线程、多线程、线程池
- 线程、多线程、线程池
- 线程(六)线程池
- ExecutorService(线程池)+线程
- 线程和线程池
- 线程&线程池 简略
- 线程和线程池
- 线程池 线程锁
- 线程、线程池总结
- 线程与线程池
- 线程、锁、线程池
- U3: Problems and solutions
- 安全漏洞(缓冲区溢出)
- 线程
- tp框架上传linix,解决pathinfo路径问题
- MySQL创建数据表
- 线程池
- cxf和spring整合出现的问题: com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Faul
- 解决ADB响应失败的N种方法!
- SuperTextView遇到的坑
- 20170611 JS实现数字反转
- STL sort 函数实现详解
- 粒子群算法--背包问题
- Tesseract OCR 引擎-概述
- Android -----有序广播