如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处:
-
降低资源消耗
-
提高响应速度
-
提高线程的可管理性。
1. Executor
Java 5中引入了Executor框架,其内部使用了线程池机制,它在java.util.cocurrent 包下
-
通过Executor来启动线程比使用Thread的start方法更好,更易管理,效率更好(用线程池实现,节约开销)
-
Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
-
有助于避免this逃逸问题
this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象
Executor 、 ExecutorService 、 Executors:
ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口,ExecutorService 还提供用来控制线程池的方法
-
Executor 接口定义了
execute()
方法用来接收一个Runnable
接口的对象,而 ExecutorService 接口中的submit()
方法可以接受Runnable
和Callable
接口的对象 -
Executor 中的
execute()
方法不返回任何结果,而 ExecutorService 中的submit()
方法可以通过一个Future对象返回运算结果 -
Executors
类提供工厂方法用来创建不同类型的线程池,以下为java中常见的四种线程池:-
Executors.newCachedThreadPool()
:缓存线程池(长度无限制,自动创建线程) -
Executors.newFixedThreadPool()
:定长线程池 (线程池已满时需要等待) -
Executors.newSingleThreadExecutor()
:单线程线程池(效果与定长线程池 创建时传入数值1效果一致) -
Executors.newScheduledThreadPool()
:周期性任务定长线程池
-
2. 缓存线程池
定义:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>() );
}
执行流程:
-
判断线程池是否存在空闲线程
-
存在则使用
-
不存在则创建线程、并放入线程池, 然后使用
示例:
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
executorService.execute(()-> System.out.println(Thread.currentThread().getName()));
}
}
3. 定长线程池
定义:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() );
}
执行流程:
-
判断线程池是否存在空闲线程
-
存在则使用
-
不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
-
不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
public class FixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
executorService.execute(()->{
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
}
4. 单线程线程池
定义:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()) );
}
执行流程:
-
判断线程池中的那个唯一的线程是否空闲
-
空闲则使用
-
不空闲, 则等待池中的单个线程空闲后再使用
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
executorService.execute(()->{
try {
Thread.sleep(1400);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
}
5. 周期性任务定长线程池
定义:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
// super: ThreadPoolExecutor
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue() );
}
执行流程:
-
判断线程池是否存在空闲线程
-
存在则使用
-
不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
-
不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
// 定时任务:(5秒后执行)
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, 5, TimeUnit.SECONDS);
// 周期任务 (5秒后开始执行,间隔2秒重复执行)
scheduledExecutorService.scheduleAtFixedRate(()->{
System.out.println(Thread.currentThread().getName());
}, 5, 2, TimeUnit.SECONDS);
}
}
方法定义:
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
参数说明:
-
Runnable command
:runnable类型的任务 -
long delay
:时长数字(延迟执行的时长) -
long period
:周期时长(每次执行的间隔时间) -
TimeUnit unit
:时长数字的单位
6. 自定义线程池
如果需要自定义线程池,可以用 ThreadPoolExecutor
类创建,它有多个构造方法来创建线程池
// 以此为例:
public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue)
-
corePoolSize
:线程池中所保存的核心线程数,包括空闲线程 -
maximumPoolSize
:池中允许的最大线程数 -
keepAliveTime
:线程池中的空闲线程所能持续的最长时间 -
unit
:持续时间的单位 -
workQueue
:任务执行前保存任务的队列,仅保存由execute方法提交的Runnable任务
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//创建等待队列
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10);
//创建线程池,池中保存的线程数为3,允许的最大线程数为5
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 5, 50, TimeUnit.MILLISECONDS, queue);
poolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
// 关闭线程池
poolExecutor.shutdown();
}
}