无论在Java的开发中还是在Android的开发中,线程都占有重要的地位,所以今天就来说说线程池的东西。
在Android开发中,我们经常把一个耗时任务放在一个线程中进行执行,目的就是为了避免ANR异常。但是如果我们在一个页面开了很多线程,线程在短时间内执行结束,我们这样频繁的创建线程就降低了系统的运行效率。所以就有了线程池。线程池的作用是什么呢?
线程池会根据系统的环境变量,自动或手动配置一个线程池中的线程数量,使线程的创建和回收达到一个理想的状态,减少了系统资源的消耗,提高系统的效率。这样我们就得出了使用线程池的几个好处:
在jdk1.5中,java给我们提供了java.util.concurrent包,这个包里就是关于我们线程池的使用接口和类。线程池的顶级接口就是Executor接口。它有一个execute(Runnable)方法用来执行所提交的Runnable任务。它只是一个接口,所以需要有它的实现体:
配置线程池是一件比较复杂的工作,如果我们对线程池的原理不是很了解,很容易导致配置的线程池达不到效果。所以系统给我们提供了Executors类,它提供一些静态方法帮助我们创建一些常用的线程池。
测试案例:
我们创建一个Text的线程,用于测试:
class Text implements Runnable{
String name;
public Text(String name){
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--" + name);
}
}
现在我们开始测试线程池的效果:
public static void main(String[]args){
//我们创建一个指定大小为3的线程池
ExecutorService exServiceFixed = Executors.newFixedThreadPool(3);
Text t1 = new Text("t1");
Text t2 = new Text("t2");
Text t3 = new Text("t3");
Text t4 = new Text("t4");
Text t5 = new Text("t5");
exServiceFixed.execute(t1);
exServiceFixed.execute(t2);
exServiceFixed.execute(t3);
exServiceFixed.execute(t4);
exServiceFixed.execute(t5);
exServiceFixed.shutdown();
}
结果:
pool-1-thread-1--t1
pool-1-thread-1--t4
pool-1-thread-1--t5
pool-1-thread-2--t2
pool-1-thread-3--t3
我们可以看到尽管我们用线程池执行了5个线程任务,但是在线程池内部仅仅有我们指定的3个线程在工作,所以达到了提高效率的作用。
public static void main(String[]args){
//我们创建一个可重用的线程池
ExecutorService exServiceCached = Executors.newCachedThreadPool();
Text t1 = new Text("t1");
Text t2 = new Text("t2");
Text t3 = new Text("t3");
Text t4 = new Text("t4");
Text t5 = new Text("t5");
exServiceCached.execute(t1);
exServiceCached.execute(t2);
exServiceCached.execute(t3);
exServiceCached.execute(t4);
exServiceCached.execute(t5);
exServiceCached.shutdown();
}
运行结果:
pool-1-thread-1--t1
pool-1-thread-2--t2
pool-1-thread-3--t3
pool-1-thread-4--t4
pool-1-thread-5--t5
线程池中开了5个线程,这个线程池的特点就是可重用,不限制大小,数量以JVM在系统中能创建的大小为准。
例如:
public static void main(String[]args){
//我们创建一个可重用的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
Text t1 = new Text("t1");
Text t2 = new Text("t2");
Text t3 = new Text("t3");
Text t4 = new Text("t4");
Text t5 = new Text("t5");
scheduledExecutorService.schedule(t1, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(t2, 2000, TimeUnit.MILLISECONDS);
}
注意,此时使用的ScheduledExecutorService,它是ExecutorService接口的实现接口。schedule方法,用来定时任务,线程会在指定时间后进行执行。
另外,它还有定时持续的任务,
scheduledExecutorService.scheduleAtFixedRate(t1, 1000,2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleAtFixedRate(t2, 1000,2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleWithFixedDelay(t1, 1000,2000, TimeUnit.MILLISECONDS);
每隔一段时间就去执行任务。
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-2--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
pool-1-thread-2--t1
pool-1-thread-1--t2
public static void main(String[]args){
//我们创建一个可重用的线程池
ExecutorService executorServiceSingle = Executors.newSingleThreadExecutor();
Text t1 = new Text("t1");
Text t2 = new Text("t2");
Text t3 = new Text("t3");
executorServiceSingle.execute(t1);
executorServiceSingle.execute(t2);
executorServiceSingle.execute(t3);
}
结果:
pool-1-thread-1--t1
pool-1-thread-1--t2
pool-1-thread-1--t3
我们可以看到只有一个线程在执行我们的任务。
这里补充一个知识点就是ExecutorService.submit()方法。这个方法有三个重载:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
什么都没实现,就一个call()方法,根据注释,这个方法用来计算某个结果。
Future的源码结构:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
也不复杂,几个方法判断任务是否结束,以及通过get方法获取任务的结果。我们直接看一个例子吧!
public class MainDemo {
public static void main(String[]args){
//我们创建一个可重用的线程池
ExecutorService executorServiceSingle = Executors.newSingleThreadExecutor();
Future<Integer> future = executorServiceSingle.submit(new AddCallable());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class AddCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//执行我们的业务逻辑处理
return 2+3;
}
}
我们实现一个Callable接口用来处理我们的任务,然后通过Future来获取任务的结果。果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。这也就是为什么需要捕捉InterruptedException异常的原因。这点是不是跟我们Android的下载任务差不多。开启一个下载任务,然后通过Handler发送的UIThread中进行处理。
同样还有一种组合Callable+FutureTask
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask);
thread.start();
本着复习知识点的原则,并没有介绍线程池的配置,大家可以去搜索相关资料学习下,复习这个的知识点,就是准备这几天写个Android网络请求的简单框架使用,所以知识点也知识简单介绍了用法,具体的深入分析没做。
参考文章:
http://blog.csdn.net/sd0902/article/details/8395677
http://www.cnblogs.com/dolphin0520/p/3949310.html
http://www.cnblogs.com/dolphin0520/p/3932921.html
http://www.iteye.com/topic/366591
======================================
作者:mr_dsw
地址:http://blog.csdn.net/mr_dsw
理念:转载注明出处,分享是进步的源泉
======================================
原文:http://blog.csdn.net/mr_dsw/article/details/51026150