Java线程池初步学习及使用
了解线程池
1.什么是线程池?
(首先要理解什么是线程)
线程池,thread pool,是一种线程使用模式,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
通俗来说,就是可管理和维护以及分配线程的“池子”。
2.为什么使用线程池?
在实际开发中,线程是宝贵资源,为了减少创建和销毁线程的次数,让每个线程都可以多次的使用,可以根据系统情况调整线程的数量,防止消耗过多内存。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,使用线程池就可以优化。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。
线程池工作原理
让我们来首先看下ThreadPoolExecutor的构造方法:
public ThreadPoolExecutor (
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadfactory,
RejectedExecutorHandler handler) {
//略,实现方法部分
}
corePoolSize :
线程池中核心线程数的最大
值
maximumPoolSize :
线程池中能拥有最多线程数
workQueue:
用于缓存任务的阻塞队列当调用线程池execute() 方法添加一个任务时,线程池会做如下判断:如果有空闲线程,则直接执行该任务;如果没有空闲线程,且当前运行的线程数少于corePoolSize,则创建新的线程执行该任务;如果没有空闲线程,且当前的线程数等于corePoolSize,同时阻塞队列未满,则将任务入队列,而不添加新的线程;如果没有空闲线程,且阻塞队列已满,同时池中的线程数小于maximumPoolSize ,则创建新的线程执行任务;如果没有空闲线程,且阻塞队列已满,同时池中的线程数等于maximumPoolSize ,则根据构造函数中的 handler 指定的策略来拒绝新的任务。
KeepAliveTime:
keepAliveTime :表示空闲线程的存活时间TimeUnit unit :表示keepAliveTime的单位当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。注:如果线程池设置了allowCoreThreadTimeout参数为true(默认false),那么当空闲(包括核心线程)线程超过keepaliveTime后直接停掉。(不会判断线程数是否大于corePoolSize)即:最终线程数会变为0。
workQueue 任务队列:
workQueue :它决定了缓存任务的排队策略ThreadPoolExecutor线程池推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue 和 ArrayBlockingQueue。
threadFactory:
threadFactory :指定创建线程的工厂。(可以不指定)如果不指定线程工厂时,ThreadPoolExecutor 会使用ThreadPoolExecutor.defaultThreadFactory 创建线程。
handler 拒绝策略:
handler :表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略。(可以不指定)
4种拒绝策略
ThreadPoolExecutor.AbortPolicy()
若线程池已满,抛出RejectedExecutionException异常。默认策略
ThreadPoolExecutor.CallerRunsPolicy()
由向线程池正在执行的worker来执行该任务
ThreadPoolExecutor.DiscardPolicy()
不报错,直接丢弃此线程
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃最旧的任务(最先提交而没有得到执行的任务)并将自己添加到队列中
线程池相关接口介绍:
1、ExecutorService接口:
该接口是真正的线程池接口。上面的ThreadPoolExecutor以及下面的ScheduledThreadPoolExecutor都是该接口的实现类。改接口常用方法:
public Future<?> submit(Runnable task):提交Runnable任务到线程池,返回Future对象,由于Runnable没有返回值,也就是说调用Future对象get()方法返回null;
public Future submit(Callable task):提交Callable任务到线程池,返回Future对象,调用Future对象get()方法可以获取Callable的返回值;
Future submit(Runnable task,T result):提交Runnable任务到线程池,返回Future对象,调用Future对象get()方法可以获取Runnable的参数值;
invokeAll(collection of tasks)/invokeAll(collection of tasks, long timeout, TimeUnit unit):invokeAll会按照任务集合中的顺序将所有的Future添加到返回的集合中,该方法是一个阻塞的方法。只有当所有的任务都执行完毕时,或者调用线程被中断,又或者超出指定时限时,invokeAll方法才会返回。当invokeAll返回之后每个任务要么返回,要么取消,此时客户端可以调用get/isCancelled来判断具体是什么情况。
invokeAny(collection of tasks)/invokeAny(collection of tasks, long timeout, TimeUnit unit):阻塞的方法,不会返回 Future 对象,而是返回集合中某一个Callable 对象的结果,而且无法保证调用之后返回的结果是哪一个 Callable,如果一个任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。和invokeAll区别是只要有一个任务执行完了,就把结果返回,并取消其他未执行完的任务;同样,也带有超时功能;
public void shutdown():在完成已提交的任务后关闭服务,不再接受新任;
public void shutdownNow():停止所有正在执行的任务并关闭服务;
public boolean isTerminated():测试是否所有任务都执行完毕了;
public boolean isShutdown():测试是否该ExecutorService已被关闭。
submit方法示例:
我们知道,线程池接口中有以下三个主要方法,接下来我们看一下具体示例:
1)Callable:
public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 300, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(50),
new ThreadFactory(){ public Thread newThread(Runnable r) {
return new Thread(r, "schema_task_pool_" + r.hashCode());
}}, new ThreadPoolExecutor.DiscardOldestPolicy());
public static void callableTest() {
int a = 1;
//callable
Future<Boolean> future = threadPool.submit(new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
int b = a + 100;
System.out.println(b);
return true;
}
});
try {
System.out.println("feature.get");
Boolean boolean1 = future.get();
System.out.println(boolean1);
} catch (InterruptedException e) {
System.out.println("InterruptedException...");
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("execute exception...");
e.printStackTrace();
}
}
2)Runnable
public static void runnableTest() {
int a = 1;
//runnable
Future<?> future1 = threadPool.submit(new Runnable(){
@Override
public void run() {
int b = a + 100;
System.out.println(b);
}
});
try {
System.out.println("feature.get");
Object x = future1.get(900,TimeUnit.MILLISECONDS);
System.out.println(x);//null
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("execute exception...");
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}