在此以前,咱们已经了解了关于线程的基本知识,今天将为各位带来,线程池这一技术。关注个人公众号「Java面典」了解更多 Java 相关知识点。html
为何使用线程池?线程池作的工做主要是控制运行的线程的数量。java
Java 中经常使用的线程池主要有四种:newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor。缓存
做用:建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程(缓存中已有 60 秒钟未被使用的线程
),若无可回收,则新建线程。多线程
特色:线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。并发
实现:框架
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(index); } }); }
做用:建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。ide
特色:性能
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
this
实现:线程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
做用:建立一个定长线程池,支持定时及周期性任务执行。
实现:
ScheduledExecutorService scheduledThreadPoo l= Executors.newScheduledThreadPool(3); scheduledThreadPool.schedule(newRunnable() { @Override public void run() { System.out.println("延迟三秒"); } }, 3, TimeUnit.SECONDS); scheduledThreadPool.scheduleAtFixedRate(newRunnable(){ @Override public void run() { System.out.println("延迟 1 秒后每三秒执行一次"); } }, 1, 3, TimeUnit.SECONDS);
做用:建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。
特色:这个线程池能够在线程死后(或发生异常时)从新启动一个线程来替代原来的线程继续执行下去。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
做用:线程复用、控制最大并发数、管理线程。线程池作的工做主要是控制运行的线程的数量,处理过程当中将任务放入队列,而后在线程建立后启动这些任务,若是线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
每个 Thread 的类都有一个 start() 方法。 当调用 start 启动线程时 Java 虚拟机会调用该类的 run() 方法。 那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。 咱们能够继承重写Thread 类,在其 start () 方法中添加不断循环调用传递过来的 Runnable 对象。 这就是线程池的实现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 以前能够是阻塞的。
通常的线程池主要分为如下 4 个组成部分:
Java 中的线程池是经过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。ThreadPoolExecutor 的构造方法以下:
/** * @param corePoolSize 指定线程池中的线程数量 * @param maximumPoolSize 指定线程池中的最大线程数量 * @param keepAliveTime 当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间 * @param unit keepAliveTime 的单位 * @param workQueue 任务队列,被提交但还没有被执行的任务 */ public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { // threadFactory 线程工厂,用于建立线程,通常用默认的便可 // handler 拒绝策略,当任务太多来不及处理,如何拒绝任务 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
做用:线程池中的线程已经用完了,没法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候咱们就须要拒绝策略机制合理的处理这个问题。
JDK 内置的拒绝策略以下:
以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍没法知足实际须要,彻底能够本身扩展 RejectedExecutionHandler 接口。
Java多线程并发03——什么是线程上下文,线程是如何调度的