Executor
执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。
public static void main(String[] args) {
new Thread(new Test117(). new MyTask()).start();//之前总是需要创建一个线程,传入runable对象然后开启线程。
}
class MyTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是任务");
}
}
execute
在该接口中只定义了一个方法。
void execute(Runnable command)
//在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
所以上面这片代码执行的另外一种:
Executor e = Executors.newSingleThreadExecutor();
//Executors在后面会讲,在这里你只要知道这个方法可以返回一个可以操作的Executor对象
e.execute(new Test117().new MyTask());
Executor的实现的子类
- AbstractExecutorService 该类还是一个抽象类
- ScheduledThreadPoolExecutor
- ThreadPoolExecutor
Executor 异步同步情况
Executor 接口并没有严格地要求执行是异步的,下面是在Java的开发文档上的一段内容。
其实看懂,还是比较容易,主要理解start和run两个方法就可以了,直接run方法是将该任务负载到调用的线程中执行(也就是主线程),这是同步,start是将该任务直接,开启一个新的线程,让后执行,这是异步。所以大家,就可以根据自己的需求,实现Executor 接口,自己根据需要来啦!
在最简单的情况下,执行程序可以在调用者的线程中立即运行已提交的任务:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
更常见的是,任务是在某个不是调用者线程的线程中执行的。以下执行程序将为每个任务生成一个新线程。
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
Executors
此类中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。可以用于上面对象的创建。相当于一个工厂类。
这个我就不说了,就是构造一个Executor对象,执行线程,用来处理runable和callable任务的。
defaultThreadFactory() 这个会默认自动为你创建线程。
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
ExecutorService es = Executors.newCachedThreadPool();
for(int i = 0;i<10;i++) {
try {
Thread.sleep(i*1000);//我们在通过线程睡眠时间长,来拖下一个线程执行的顺序。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
es.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
System.out.println("我是当前执行的线程"+Thread.currentThread().getName());
return "我是执行返回";
}
});
}
es.shutdown();
}
参考学习:https://www.cnblogs.com/Steven0805/p/6393443.html
由结果可以看出,这段代码由于每一个线程执行的间隔大,后者执行开始,前者已经完成,故重用之前的线程。
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService es = Executors.newFixedThreadPool(3);
for(int i = 0;i<10;i++) {
es.submit(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("线程"+Thread.currentThread().getName()+"正在工作");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
es.shutdown();
}
由结果可以看出,执行中,线程操作数始终保持在你设置的最大线程数,如果超出,就在队列中等待。
创建一个定长线程池,支持定时及周期性任务执行。
ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
for(int i = 0;i<5;i++) {
es.schedule(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是延迟执行的任务");
}
}, 3,TimeUnit.SECONDS);//前面是延迟的时间数,后面是时间数是按照哪种形式
}
es.shutdown();
这个有点像定时设定的任务
ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
for(int i = 0;i<5;i++) {
es.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是延迟定时执行的任务");
}
}, 1,3,TimeUnit.SECONDS);//表示延迟1秒后,每3秒执行一次
}
es.shutdown();
Callable
这是一个接口,这个接口和runable接口,很类似,都是用于被线程执行而设计的,Callable和Runable 最大的区别就是:它能返回结果,并且能抛出异常。接口中只有一个不带参数的方法。
Callable 这个V就是Callable返回结果的类型。
V call() throws Exception;
Future
这是一个接口,主要用于获取异步线程的结果,常常与Callable一起使用,因为Callable只是实现了可以返回一个值,必须还要通过Future接口的get方法才能取出来。
Future 这个泛型是返回的结果类型。
这是api上的一段说明:
它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future
FutureTask
这个是Future的实现的子类,这个类实现了runable接口,所以他可以作为runable对象传到线程中执行。
可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
构造方法
这里如果是处理的是runable对象,则我们需要将最后返回的结果用参数传给FutureTask ,然后它会在执行之后返回出来。
基于runable
class MyTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
FutureTask<String> ft = new FutureTask<>(new Test118().new MyTask(),"ok");
new Thread(ft).start();
//在这里你可以处理其他的业务逻辑,然后在获取线程的值。
try {
System.out.println("我是取出来的结果"+ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
基于callable的处理
//启用一个新的线程随机产生一个随机数,然后返回。
FutureTask<Integer> ft = new FutureTask<>(new Test118().new MtTask());
new Thread(ft).start();
//在这里你可以处理其他的业务逻辑,然后在获取线程的值。
try {
System.out.println("我是取出来的结果"+ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class MtTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
Thread.sleep(3000);
return new Random().nextInt();
}
}
ExecutorService
提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。
可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。shutdown() 方法在终止前允许执行以前提交的任务,而 shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。终止时,执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。应该关闭未使用的 ExecutorService 以允许回收其资源。
常用方法
执行线程池中所有等待的callable任务,并返回Future结果的集合
这里的submit提交执行的runable或者callable的任务,并且返回一个Future结果。
方法 invokeAny 和 invokeAll 是批量执行的最常用形式,它们执行任务 collection
基于ExecutorService例子
ExecutorService es = Executors.newCachedThreadPool();
//创建了一个线程池
List<Future<Integer>> list = new ArrayList();
for(int i=0;i<10;i++) {
Future<Integer> ft = es.submit(new Test119().new MyTask(i));
//构建了十个任务
list.add(ft);
//将每个人物返回的Future结果,保存到集合
}
es.shutdown();//执行完了之后,不接受任务,这就意味着,它就等待着回收资源。
//遍历List中的Future结果集
for(int i = 0;i<list.size();i++) {
try {
System.out.println(list.get(i).get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class MyTask implements Callable<Integer>{
int number;
public MyTask(int number) {
super();
this.number = number;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
System.out.println("我是线程"+Thread.currentThread().getName());
Thread.sleep(2000);
return (int) (Math.random()*10);
}
}
代码简单,只要是感受一下,在这十个人物执行过程中,我们不需要手动创建线程去操作。