在JDK5以后,对于线程池的功能需求一般情况下都可以通过java.util.concurrent包里提供的API满足。其中可以通过java.util.concurrent.ExecutorService接口、子接口、及其实现类来实现一般情况下的线程池功能;通过java.util.concurrent.SchduledExecutorService接口、子接口、及其实现类来实现具有时间安排的线程池功能。但在学习线程池之前,有必要先了解java.util.concurrent.Executor接口的内容。
Executor
package java.util.concurrent;
public interface Executor{
void execute(Runnable command);
}
作用:将Runnable的指定与实际如何执行分离。
Demon
//Executor使用举例
import static java.lang.System.in;
import static java.lang.System.out;
import java.util.concurrent.Executor;
import java.lang.Runnable;
//异步的执行者
class SynchronizedExecutor implements Executor{
public void execute(Runnable command){
new Thread(command).start();
}
}
//同步的执行者
class LocalExecutor implements Executor{
public void execute(Runnable command){
command.run();
}
}
public class Temp {
public static void main(String... args){
Executor synchronizedExecutor = new SynchronizedExecutor();
Executor localExecutor = new LocalExecutor();
synchronizedExecutor.execute(()->{
int n=30;
while(n-->0)
out.println("synchronized msg");
});
localExecutor.execute(()->{
int n=20;
while(n-->0)
out.println("local msg");
});
out.println("flag,这句话一定出现在所有的local msg下面");
}
}
ExecutorService
package java.util.concurrent;
public interface ExecutorService extends Executor{
...
}
作用:用于满足线程池这里服务的行为需求。
用法:
(1)抽象类AbstractExecutorService实现了该接口,并且其子类java.util.concurrent.ThreadPoolExecutor提供有多种不同的构造函数,可以满足一般情况下创建线程池的需求。所以创建一个线程池的方法之一就是生成一个ThreadPoolExecutor对象,并通过其构造函数来指定该线程池的各个参数。
//构造函数参数详情,可以参考JDK开发文档
(2)最简洁的方法是,使用java.util.concurrent.Executors所提供的几个静态方法,通过调用它们可以返回操作了ExecutorService接口的对象,继而可以通过该对象调用ExecutorService所约定的方法。
Executors.newCachedThreadPool() //无限线程池,具有自动线程回收
Executors.newFixedThreadPool(int) //固定数量的线程池
Executors.newSingleThreadExecutor() //单个后台线程
Demo
//ExecutorService使用举例
import static java.lang.System.in;
import static java.lang.System.out;
import java.util.concurrent.*;
import java.lang.Runnable;
import java.io.*;
import java.net.URL;
class PagesDownload {
URL[] urls;
ExecutorService executorService;
public PagesDownload(URL[] tu, ExecutorService es){
this.urls = tu;
this.executorService = es;
}
public void download(){
if(null == urls || null == executorService) return;
for(int i=0; i<urls.length; i++){
URL tempURL = urls[i];
String fileName = ""+i+".html";
//executorService.execute(()->{
executorService.submit(()->{
try{
PagesDownload.dataCopy(tempURL.openStream(), new FileOutputStream(fileName));
}catch(IOException e){
throw new RuntimeException(e);
}
});
}
}
private static void dataCopy(InputStream src, OutputStream dest)throws IOException{
try(InputStream input = src; OutputStream output = dest){
byte[] bytes = new byte[2048];
int len;
while((len = input.read(bytes))!=-1){
output.write(bytes, 0, len);
}
}
}
}
public class Temp {
public static void main(String... args) throws Exception{
//Demo中,你可以指定自己的路径及文件
URL[] urls = {new URL("file:///D:/TestArea/index.html"), new URL("http://www.baidu.com")};
ExecutorService es = Executors.newCachedThreadPool();
new PagesDownload(urls, es).download();
es.shutdown();
}
}
ScheduledExecutorSerivce
package java.util.concurrent;
public interface ScheduledExecutorService extends ExecutorService{
...
}
作用:用于提供具有周期线程性质的线程池服务。
用法:
(1)java.util.concurrent.ScheduledThreadPoolExecutor是继承了ThreadPoolExecutor且操作了ScheduledExecutorService接口的一个类,具有线程池与排程功能。可以通过创建该类的对象来构建周期线程池服务。在其构造函数的形参上可以指定相关参数,以进行更细致的线程池服务创建。
//构造函数参数详情,可以参考JDK开发文档
(2)同样的,java.util.concurrent.Executors也为开发者提供了便捷的静态方法,用于直接创建并返回一个操作了ScheduledExecutorService接口的对象,开发者可以通过它完成具有周期性质的线程池服务的部署。
newScheduledThreadPool(int corePoolSize) //创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
newSingleThreadScheduledExecutor() //创建一个单线程执行器,可以调度命令在给定的延迟之后运行,或定期执行。
Demo
//ScheduledExecutorService使用举例
import java.util.concurrent.*;
import static java.lang.System.out;
public class Temp{
public static void main(String... args){
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(()->{
out.println(new java.util.Date());
try{
Thread.sleep(2000); //假定这里工作2秒
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
*ps:java.util.concurrent.Callable接口与java.util.concurrent.Future接口
在使用ExecutorService的submit()、invokeAll()、invokeAny()等方法时,可以发现,其重载函数支持传递Callable的操作对象,所有这里简单介绍一下Callable与Runnable的区别。
package java.util.concurrent;
public interface Callable<V>{
V call() throws Exception;
}
Callable与Runnable类似,都可以定义执行流程,但Runnable的run()方法无法返回值,且不能抛出受检异常,而Callable恰恰是对其进行了补充,Callable的call()方法不仅可以有返回值,而且还可以抛出受检异常。至于如何获取Callable的值,这需要通过与Future接口搭配使用才能做到。
Future接口被定义为对未来情况的引用,其定义有如下方法:
boolean cancel(boolean mayInterruptIfRunning) //尝试取消执行此任务。
V get() //等待计算完成,然后检索其结果。
V get(long timeout, TimeUnit unit) //等待给定时间,然后获取其结果。
boolean isCancelled() //如果此任务在正常完成之前被取消,则返回 true 。
boolean isDone() //返回 true如果任务已完成。
java.util.concurrent.FutureTask<V>的构造函数可以接受操作了Callable接口的对象,而其操作的接口RunnableFuture继承了Runnable与Future,所以其对象可以被用来构造Thread,从而实现异步执行指定过程的同时,异步地获取指定过程的处理结果。如下面的Demo:
import java.util.concurrent.*;
import static java.lang.System.*;
public class Temp{
public static void main(String... args) throws Exception{
FutureTask<String> targetFuture = new FutureTask<String>(()->{int i=10; while(i-->0) Thread.sleep(500); return "脑力不足请求支援";});
out.println("101区脑细胞负责学习");
new Thread(targetFuture).start();
while(!targetFuture.isDone()){
out.println("102区脑细胞负责打游戏");
}
out.println("101区脑细胞的学习结果:"+targetFuture.get());//直接调用get()会被阻塞
}
}
如有不当,望留言指明,非常感谢!!!