网上讲了很多的关于线程池的例子。其实在我们实际应用中,譬如说,一个应用的线程是怎样来管理的,我们就可以说,我们可以使用线程池来管理线程。

eg:

class DianLiang1 implements Runnable {

@Override

punlic void Run{

system.println("*************111111111111111111111****************");

}

}

class DianLiang2 implements Runnable{

@Override

punlic void Run{

system.println("***************222222222222222222222222**************");

}

}

class DianLiang3 implements Runnable {

@Override

punlic void Run{

system.println("***************222222222222222222222222**************");

}

}

public void main(){ public static void main(String[] args) {
DianLiang1t1 = new DianLiang1(); DianLiang1t1 = new DianLiang1();
DianLiang1t1 = new DianLiang1();
t1 .start();
t2.start(); t3.start(); }
} 这样问题就来了:

如果说:在住入口里面:

倘若有多个线程,有100个线程这样线程就无法进行控制了。

这个时候我们最好用线程池来管理线程。

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

比如:一个应用要和网络打交道,有很多步骤需要访问网络,为了不阻塞主线程,每个步骤都创建个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只需要创一个线程池,把这些步骤像任务一样放进线程池,在程序销毁时只要调用线程池的销毁函数即可。


java常用的线程池有三种:
  • newFixedThreadPool
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
  ExecutorService pool = Executors.newFixedThreadPool(2);
  //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
  Thread t1 = new MyThread();
  Thread t2 = new MyThread();
  Thread t3 = new MyThread();
  Thread t4 = new MyThread();
  Thread t5 = new MyThread();
  //将线程放入池中进行执行
  pool.execute(t1);
  pool.execute(t2);
  pool.execute(t3);
  pool.execute(t4);
  pool.execute(t5);

  • newSingleThreadExecutor
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
  • newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。


以下是 转载内容:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,

TimeUnit unit,BlockingQueue<Runnable>workQueue,RejectedExecutionHandler handler)

corePoolSize: 线程池维护线程的最少数量 maximumPoolSize:线程池维护线程的最大数量keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位 workQueue: 线程池所使用的缓冲队列handler: 线程池对拒绝任务的处理策略

一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

l如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

2如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。

3如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

4如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

5当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

一个ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用Executors工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个ThreadPoolExecutor还维护着一些基本的统计数据,如完成的任务数。

为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:

核心和最大池大小

ThreadPoolExecutor将根据corePoolSize(参见getCorePoolSize())和maximumPoolSize(参见getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法execute(java.lang.Runnable)中提交时,如果运行的线程少于corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于corePoolSize而少于maximumPoolSize,则仅当队列满时才创建新线程。如果设置的corePoolSize和maximumPoolSize相同,则创建了固定大小的线程池。如果将maximumPoolSize设置为基本的无界值(如Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)进行动态更改。

按需构造

默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法prestartCoreThread()或prestartAllCoreThreads()对其进行动态重写。

创建新线程

使用ThreadFactory创建新线程。如果没有另外说明,则在同一个ThreadGroup中一律使用Executors.defaultThreadFactory()创建线程,并且这些线程具有相同的NORM_PRIORITY优先级和非守护进程状态。通过提供不同的ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从newThread返回null时ThreadFactory未能创建线程,则执行程序将继续运行,但不能执行任何任务。

保持活动时间

如果池中当前有多于corePoolSize的线程,则这些多出的线程在空闲时间超过keepAliveTime时将会终止(参见getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法setKeepAliveTime(long, java.util.concurrent.TimeUnit)动态地更改此参数。使用Long.MAX_VALUE TimeUnit.NANOSECONDS的值在关闭前有效地从以前的终止状态禁用空闲线程。

排队

所有BlockingQueue都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

A.如果运行的线程少于corePoolSize,则Executor始终首选添加新的线程,而不进行排队。

B.如果运行的线程等于或多于corePoolSize,则Executor始终首选将请求加入队列,而不添加新的线程。

C.如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:

直接提交。工作队列的默认选项是SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界maximumPoolSizes以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的LinkedBlockingQueue)将导致在所有corePoolSize线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的maximumPoolSizes时,有界队列(如ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低CPU使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

被拒绝的任务

当Executor已经关闭,并且Executor将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法execute(java.lang.Runnable)中提交的新任务将被拒绝。在以上两种情况下,execute方法都将调用其RejectedExecutionHandler的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)方法。下面提供了四种预定义的处理程序策略:

A.在默认的ThreadPoolExecutor.AbortPolicy中,处理程序遭到拒绝将抛出运行时RejectedExecutionException。

B.在ThreadPoolExecutor.CallerRunsPolicy中,线程调用运行该任务的execute本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

C.在ThreadPoolExecutor.DiscardPolicy中,不能执行的任务将被删除。

D.在ThreadPoolExecutor.DiscardOldestPolicy中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

定义和使用其他种类的RejectedExecutionHandler类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

挂钩方法

此类提供protected可重写的beforeExecute(java.lang.Thread, java.lang.Runnable)和afterExecute(java.lang.Runnable, java.lang.Throwable)方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法terminated()来执行Executor完全终止后需要完成的所有特殊处理。

如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

队列维护

方法getQueue()允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable)和purge()这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

更多相关文章

  1. Android的线程使用来更新UI----Thread、Handler、Looper、TimerT
  2. Android之Handler用法总结
  3. android handler
  4. Android关于线程更新UI的方法
  5. 解析Android中的main线程与子线程
  6. Android,UI主线程与子线程
  7. Android(安卓)线程优先级设置方法
  8. 理解 Android(安卓)消息机制
  9. Android(安卓)的消息队列模型

随机推荐

  1. 《Qt on Android核心编程》相关资源
  2. 新手怎样画线稿?漫画人物线稿绘画教程
  3. k8s单master集群部署
  4. 阿里app法人认证备案流程
  5. 漫画头发怎么画好?漫画人物头发画法
  6. 领略 Laravel 和 Filament 后台的魔力
  7. Linux 自动化构建工具 make/Makefile
  8. 日系插画入门学什么?新手插画学习方法
  9. Android中native进程内存泄露的调试技巧
  10. 深入浅出 - Android系统移植与平台开发(六