此文章接着上篇文章:Java多线程概念浅析
本篇博客包含如下内容:
- 为何有线程池?Java对线程池的支持?
- 线程池实例!
- 线程池不同创建方式的意义?
- 何时使用线程池?
为何有线程池?
为何有线程池?
- 上篇文章中提到线程的创建和简单的解析,但是我们会发现,我们必须要为每一任务都建立一个线程。
- 因此对于大量的任务而言这不是高效的,为每个任务建立线程可能会限制流量并且造成性能的降低。
- 线程池则为我们提供了管理并发的执行多个任务的理想方法?
对于线程池任务和线程管理在Java中提供的方法!
- 再简单理解任务与线程:任务是Runnable接口的实例,线程的本质是便于任务执行的对象。
- Java提供Executor接口来执行线程池中的任务
- 提供ExecutorService接口来管理和控制任务
- ExecutorService是Executor的子接口
- 创建执行线程池中的任务(Executor)的对象,使用Executors类中的静态方法newFixedThreadPool(numberOfThreads:int):ExecutorService和newCachedThreadPool():ExecutorService
两个接口的源码学习
Executor接口:
package java.util.concurrent;
public interface Executor {
void execute(Runnable arg0);
}
ExecutorService接口:
package java.util.concurrent;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long arg0, TimeUnit arg2) throws InterruptedException;
<T> Future<T> submit(Callable<T> arg0);
<T> Future<T> submit(Runnable arg0, T arg1);
Future<?> submit(Runnable arg0);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg3)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> arg0) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg3)
throws InterruptedException, ExecutionException, TimeoutException;
}
几个重要方法解析:
所属接口/类 | 方法 | 解析 |
---|---|---|
Executors | newFixedThreadPool(int numberOfThreads):ExecutorService | 创建一个可并发执行固定数目线程的线程池 |
…… | newCachedThreadPool():ExecutorService | 创建一个可按需改变可执行线程数的线程池 |
Executor | execute(Runnable object):void | 执行传入的任务,子接口都实现此方法 |
ExecutorService | submit() | 向线程池提交任务 |
…… | shutdown():void | 关闭执行器,但允许完成完成执行器中的任务,一旦关闭不能接受新任务 |
…… | shutdownNow():List | 立即关闭,不管是否存在未完成的任务,返回未完成的清单 |
…… | isShutdown():boolean | 如果执行器已经关闭则返回true |
…… | isTerminated():boolean | 如果线程池中所有任务都被终止则返回true |
线程池实例
任务代码参考:Java多线程概念浅析
线程池代码:
package executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import thread.RunnableImp1;
import thread.RunnableImp2;
public class ExecutorDemo {
public static void main(String[] args) {
// 创建指定最大线程数的线程池执行器
ExecutorService executor = Executors.newFixedThreadPool(3);
// 创建任务并添加到线程池,执行器会主动创建这三个任务
executor.execute(new RunnableImp1('a',100));
executor.execute(new RunnableImp1('b',100));
executor.execute(new RunnableImp2(100));
// 关闭执行器
executor.shutdown();
// 执行器是否已经关闭
boolean isClose = executor.isShutdown();
// 换行
System.out.println();
System.out.println(isClose);
}
}
输出:
aaaaabb1 a2 b3 a4 b5 a6 b7 a8aaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaab
执行器结束?-true
如果将:
ExecutorService executor = Executors.newFixedThreadPool(3);
中的3改为1,输出结果是?
- 这三个任务线程会顺序执行,因为只有一个可执行线程!下个任务会等待上个任务执行完重用此线程。
线程池不同创建方式的意义
newFixedThreadPool(int)创建:
- 当使用此方法创建时,创建固定的可执行线程,如果线程完成了某个任务的执行,它可被重新使用以执行另外的任务。如果线程池中所有的线程都不是处于空闲状态。而且还有任务在等待执行,那么在关机之前,如果由于一个错误终止了一个线程,就会创建一个新的线程来替代它。
newCachedThreadPool()创建:
- 当使用此方法创建时,创建的可执行线程是可变的。如果线程池中所有的线程都不是处于空闲状态。而且还有任务在等待执行,此方法会创建一个新线程。如果缓冲池中的线程在60秒内都没有被使用就该终止它。
何时使用线程池
- 如果需要为一个任务创建一个线程、则使用Thread类,如果需要为多个任务创建线程,最好使用线程池。