为什么要使用线程池?
平时我们在使用多线程的时候,通常都是架构师配置好了线程池的 Bean,我们需要使用的时候,提交一个线程即可,不需要过多关注其内部原理。
在学习一门新的技术之前,我们还是先了解下为什么要使用它,使用它能够解决什么问题:
- 创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率
例如:
记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3
如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!
正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销
- 线程并发数量过多,抢占系统资源从而导致阻塞
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况
运用线程池能有效的控制线程最大并发数,避免以上的问题
- 对线程进行一些简单的管理
比如:延时执行、定时循环执行的策略等
运用线程池都能进行很好的实现
创建一个线程池
在 Java 中,新建一个线程池对象非常简单,Java 本身提供了工具类java.util.concurrent.Executors
,可以使用如下代码创建一个固定数量线程的线程池:
ExecutorService service = Executors.newFixedThreadPool(10);
注意:以上代码用来测试还可以,实际使用中最好能够显示地指定相关参数。
我们可以看下其内部源码实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Runnable和Callable
可以向线程池提交的任务有两种:Runnable
和Callable
,二者的区别如下:
- 方法签名不同,
void Runnable.run()
,V Callable.call() throws Exception
- 是否允许有返回值,
Callable
允许有返回值 - 是否允许抛出异常,
Callable
允许抛出异常。
Callable
是JDK1.5时加入的接口,作为Runnable
的一种补充,允许有返回值,允许抛出异常。
三种提交任务的方式:
提交方式 | 是否关心返回结果 |
---|---|
Future<T> submit(Callable<T> task) | 是 |
void execute(Runnable command) | 否 |
Future<?> submit(Runnable task) | 否,虽然返回Future,但是其get()方法总是返回null |
我们编写个程序来方便理解:
ExecutorService executorService= Executors.newFixedThreadPool(2);//创建容量2的线程池
for(int i=0;i<10;i++){
Thread thread=new ThredPoor(i);
System.out.println("id"+i);
executorService.execute(thread);
}
executorService.shutdown(); //关闭线程池
- Executors
通过 newFixed ThreadPool:返回个指定容量为2( NUMBER)的线程池。如何理解线程池容量为2呢?在fo循环里面,创建出了10个线程,然后由ExecutorService的 execute执行这10个线。但是,由于在 newFixedThreadPool中已经指定了线程池的容量为2,所以,几同时创建的10个线程虽然已经放入了线程池,然而并不会立即得到执行,而是在线程池的调度服务器 ExecutorService的管制下,进入待执行的线程队列中。然后线池先执行2个线程任务,当这2个线程任务执行完毕后,Java线程池将会依次释出2个线程再继续执行,就这样每次执行指定容量大小的线程任务条数。
线程调度 schedule
public ScheduledFuture<?> schedule(Runnable command, long delay,TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<Void> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit),
sequencer.getAndIncrement()));
delayedExecute(t);
return t;
}
该函数常用来调度运行单个线程任务:
public class Schedule implements Runnable{
private String tAG;
public Schedule(String tAG) {
this.tAG = tAG;
}
@Override
public void run() {
System.out.println(tAG);
}
public static void main(String[] args) {
ScheduledThreadPoolExecutor s=new ScheduledThreadPoolExecutor(2);
Schedule thred1=new Schedule("guo");
Schedule thred2=new Schedule("da");
s.schedule(thred1,3, TimeUnit.SECONDS); //延迟3秒执行
s.schedule(thred2,5, TimeUnit.SECONDS);
}
}
Future,Callable获取线程返回结果:
public class CallThred implements Callable<String> {
int id;
public CallThred(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
System.out.println("线程"+id+"开始");
Thread.sleep(2000);
System.out.println("线程"+id+"结束");
return "返回"+id;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
/* ExecutorService m= Executors.newFixedThreadPool(3);
ArrayList<Future<String>> mf=new ArrayList<Future<String>>();
for(int i=0;i<8;i++){
CallThred ct=new CallThred(i);
Future<String> fs=m.submit(ct);
mf.add(fs);
}
System.out.println("获取结果");
for(Future<String> fs:mf){
System.out.println(fs.get());
}
System.out.println("获取结果结束");*/
ArrayList<FutureTask<String>> mf=new ArrayList<FutureTask<String>>();
for(int i=0;i<8;i++){
CallThred ct=new CallThred(i);
FutureTask<String> ft=new FutureTask<String>(ct);
mf.add(ft);
new Thread(ft).start();
}
System.out.println("获取结果");
for(FutureTask<String> fs:mf){
System.out.println(fs.get());
}
System.out.println("获取结果结束");
}
}