线程池
- 在Java中线程池被称为ExecutorService(执行服务)但是在底层还是被称为ThreadPools
Executor框架创建线程池
Executor框架包括:线程池、Executor、Executors、ExecutorService、 CompletionService,Future,Callable 等
通过Executors类中提供的一个方法newFixedThreadPool得到一个线程对象
我们首先来看一下整个Executors创建线程池的体系
1.public static ExecutorService newFixedThreadPool(int nThreads): 创建一个线程池并返回
2.同样也可以直接创建ThreadPooExecutor对象并注入参数即可
/* 格式如下: */
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize核心线程数,
maxmumPoolSize最大线程数,
keepAliveTime普通线程存活时间,
unit时间单位,
BlockQueue组赛队列,
handler拒绝提交任务异常);
Executers提供静态方法–>newFixedThreadPool()—>底层由ThreadPoolExecutor实现
首先我们先看一下newFixedThreadPool的底层实现
newFixedThreadPool源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/*可以看出在newFixedThreadPool的底层依然是创建了 ThreadPoolExecutor(线程执行器)
下面再看一下ThreadPoolExecutor的底层
那么下面我们再来看一看ThreadPoolExecutor的源码:
public ThreadPoolExecutor(int corePoolSize, //--->核心线程(线程池保留这个数量的线程不死)
int maximumPoolSize, //--->最大线程(线程池所支持的做多线程数量)假设核心线程3最大线程10则有7个线程用完就会死
long keepAliveTime, //--->线程保持存活时间(KeepAliveTime就是闲置的线程等待的最长时间,超过这个时间改线程就会消亡)
TimeUnit unit, //--->时间单位
BlockingQueue<Runnable> workQueue//--->(阻塞队列)等待队列,正在排队等待执行的线程存放在该队列中等待线程执行
) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
创建线程池,我们已经指定了核心线程为3,其他在为指定的条件下均为默认情况,此时我们添加三个任务均会创建新县城执行,但是添加第四个任务时就不会再创建新的线程,但是具体是由哪个线程执行第四个任务具体还要看线程调度的安排
public class _2 {
public static void main(String[] args) {
/* 1. 创建线程池,此处只指定了线程数,其余均为默认*/
ExecutorService pools = Executors.newFixedThreadPool(3);
/* 3. 添加任务到线程池 */
MyRunnable task = new MyRunnable();
pools.execute(task);//第一次提交任务,线程池此时创建新线程
pools.execute(task);//第二次提交任务,线程池此时创建新线程
pools.execute(task);//第三次提交任务,线程池此时创建新线程
pools.execute(task);//第四次提交任务,复用之前的线程
}
}
/* 2. 创建线程任务类 此处用于给线程池添加任务 */
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
ThreadPool执行Callable任务
此时我们将Runnable和Callable两种线程任务类做出对比
public class Demoo {
public static void main(String[] args) {
/* 1. 创建线程池--->核心线程数为3 */
ExecutorService pools = Executors.newFixedThreadPool(3);
/* 2. 创建Callable任务并添加到线程池中 */
MyCallable mc = new MyCallable();
try {
System.out.println(pools.submit(mc).get());
/*调用submit返回的是一个Future类实例,故而可以直接调用get方法来获取返回值
//此处体现Callable的优越性,可以得到返回的结果
*/
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<String>{
@Override
public String call() throws InterruptedException {
//此处体现Callable可以直接抛出异常的优越性
Thread.sleep(1000);
return "call方法体执行中...";
}
}
详解submit()和execute()方法
浅谈一下向线程池添加任务的两种方法submit()和execute()
submit():用于添加带有返回值的任务—>例如Callable任务
也可以用于提交Runnable不带返回值的任务
通过获取返回值结果可以确定线程池执行此任务是否成功
submit()会得到线程池返回的一个Future对象,可以直接调用get()方法来获取线程任务返回值
下面我们来看一下submit()方法源码
/* 在接口ExecutorService中声明 */
<T> Future<T> submit(Callable<T> task);
/* 下面看在AbstractExecutorService实现方法的方法体 */
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
//此处返回的ftask是Fture的一个对象
}
//再看RunnableFuture继承了Future类
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
/*
综上我们可以得出它最终返回了一个Future的实例*/
execute():用于提交不需要返回值的任务—>例如Runnable任务 因此并不能判断我们的线程池是否执行成功
关闭线程池
pools.shutdown();//等线程池任务执行完毕就会关闭
pools.shutdownNow();//立即关闭线程池,不论任务是否执行完毕