Java Executor并发框架〔一〕
一、概述
Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创立在高并发及大数据量是非常消耗资源的,因为java提供了线程池。在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在JDK1.5后,有了很大的改善。JDK1.5之后参加了包,包的参加给予开发人员开发并发程序以及解决并发问题很大的帮助。这篇文章主要介绍下并发包下的Executor接口,Executor接口虽然作为一个非常旧的接口〔JDK1.5 2004年发布〕,但是很多程序员对于其中的一些原理还是不熟悉,因此写这篇文章来介绍下Executor接口,同时稳固下自己的知识。假设文章中有出现错误,欢迎大家指出。
二、Executors工厂类
对于数据库连接,我们经常听到数据库连接池这个概念。因为建立数据库连接时非常耗时的一个操作,其中涉及到网络IO的一些操作。因此就想出把连接通过一个连接池来管理。需要连接的话,就从连接池里取一个。当使用完了,就“关闭〞连接,这不是正在意义上的关闭,只是把连接放回到我们的池里,供其别人在使用。所以对于线程,也有了线程池这个概念,其中的原理和数据库连接池是差不多的,因此java jdk 中也提供了线程池的功能。
线程池的作用:线程池就是限制系统中使用线程的数量以及更好的使用线程
根据系统的运行情况,可以自动或手动设置线程数量,到达运行的最正确效果:配置少了,将影响系统的执行效率,配置多了,又会浪费系统的资源。用线程池配置数量,其他线程排队等候。当一个任务执行完毕后,就从队列中取一个新任务运行,假设没有新任务,那么这个线程将等待。假设来了一个新任务,但是没有空闲线程的话,那么把任务参加到等待队列中。
为什么要用线程池:
1.减少线程创立和销毁的次数,使线程可以屡次复用
2.可以根据系统情况,调整线程的数量。防止创立过多的线程,消耗过多的内存〔每个线程1M左
右〕
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
比较重要的几个类:
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1. newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创立一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。假设这个唯一的线程因为异常完毕,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2. newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创立固定大小的线程池。每次提交一个任务就创立一个线程,直到线程到达线程池的最大大小。线程池的大小一旦到达最大值就会保持不变,在提交新任务,任务将会进入等待队列中等待。假设某个线程因为执行异常而完毕,那么线程池会补充一个新线程。
3. newCachedThreadPool
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
创立一个可缓存的线程池。假设线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲〔60秒处于等待任务到来〕的线程,当任务数增加时,此线程池又可以智能的添加
新线程来处理任务。此线程池的最大值是Integer的最大值(2^31-1)。
4. newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创立一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
实例:
注:使用了java8的lambda表达式以及stream
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executor = wSingleThreadExecutor();
IntStream.range(0, 5).forEach(i -> ute(() -> {
String threadName = Thread.currentThread().getName();
("finished: " + threadName);
}));
try {
//clo pool
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (!executor.isTerminated()) {
executor.shutdownNow();
}
}
}
}
输出结果:
finished: pool-1-thread-1
finished: pool-1-thread-1
finished: pool-1-thread-1
finished: pool-1-thread-1
finished: pool-1-thread-1
线程名都一样,说明是同一个线程
public class FixedThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executor = wFixedThreadPool(3);
IntStream.range(0, 6).forEach(i -> ute(() -> { try {
(1);
String threadName = Thread.currentThread().getName();
("finished: " + threadName);
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
//clo pool 同上...
}
}
输出结果:
finished: pool-1-thread-2
finished: pool-1-thread-3
finished: pool-1-thread-1
finished: pool-1-thread-1
finished: pool-1-thread-2
finished: pool-1-thread-3
只创立了三个线程
public class CachedThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executor = wCachedThreadPool(); IntStream.range(0, 6).forEach(i -> ute(() -> { try {
(1);
String threadName = Thread.currentThread().getName(); ("finished: " + threadName);
} catch (InterruptedException e) {
e.printStackTrace();
}