ExecutorService中submit和execute的区别
在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动、调度、管理线程的一大堆API了。在Java5以后,通过
Executor来启动线程比用Thread的start()更好。在新特征中,可以很容易控制线程的启动、执行和关闭过程,还可以很容易使用线程池的特
性。
一、创建任务
任务就是一个实现了Runnable接口的类。
创建的时候实run方法即可。
二、执行任务
通过java.util.concurrent.ExecutorService接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭
ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,也没有任务在等待执行,并且无法提交新任
务。
executorService.execute(new TestRunnable());
1、创建ExecutorService
通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();
2、将任务添加到线程去执行
当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。
三、关闭执行服务对象
executorService.shutdown();
四、综合实例
package concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by IntelliJ IDEA.
*
* @author leizhimin 2008-11-25 14:28:59
*/
publicclass TestCachedThreadPool {
publicstaticvoid main(String[] args) {
// ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(5);
// ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i
executorService.execute(new TestRunnable());
System.out.println("************* a" + i + " *************");
}
executorService.shutdown();
}
}
class TestRunnable implements Runnable {
publicvoid run() {
System.out.println(Thread.currentThread().getName() + "线程被调用了。");
while (true) {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
************* a0 *************
************* a1 *************
pool-1-thread-2线程被调用了。
************* a2 *************
pool-1-thread-3线程被调用了。
pool-1-thread-1线程被调用了。
************* a3 *************
************* a4 *************
pool-1-thread-4线程被调用了。
pool-1-thread-5线程被调用了。
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
......
五、获取任务的执行的返回值
在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被
ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只
能通过ExecutorService的( task) 方法来执行,并且返回一个 ,是表示任
务等待完成的 Future。
public interface Callable
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
类包含一些从其他普通形式转换成 Callable 类的实用方法。
Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。
当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。
同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。
遗憾的是,在Java API文档中,这块介绍的很糊涂,估计是翻译人员还没搞清楚的缘故吧。或者说是注释不到位。下面看个例子:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* Callable接口测试
*
* @author leizhimin 2008-11-26 9:20:13
*/
publicclass CallableDemo {
publicstaticvoid main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List resultList = new ArrayList();
//创建10个任务并执行
for (int i = 0; i
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future future = executorService.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
resultList.add(future);
}
//遍历任务的结果
for (Future fs : resultList) {
try {
System.out.println(fs.get()); //打印各个线程(任务)执行的结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。
executorService.shutdown();
}
}
}
}
class TaskWithResult implements Callable {
privateint id;
public TaskWithResult(int id) {
this.id = id;
}
/**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。
*
* @return
* @throws Exception
*/
public String call() throws Exception {
System.out.println("call()方法被自动调用,干活!!! " + Thread.currentThread().getName());
//一个模拟耗时的操作
for (int i = 999999; i > 0; i--) ;
return"call()方法被自动调用,任务的结果是:" + id + " " + Thread.currentThread().getName();
}
}
运行结果:
call()方法被自动调用,干活!!! pool-1-thread-1
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-5
call()方法被自动调用,任务的结果是:0 pool-1-thread-1
call()方法被自动调用,任务的结果是:1 pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,任务的结果是:2 pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,任务的结果是:3 pool-1-thread-4
call()方法被自动调用,任务的结果是:4 pool-1-thread-5
call()方法被自动调用,任务的结果是:5 pool-1-thread-6
call()方法被自动调用,任务的结果是:6 pool-1-thread-2
call()方法被自动调用,任务的结果是:7 pool-1-thread-6
call()方法被自动调用,任务的结果是:8 pool-1-thread-4
call()方法被自动调用,任务的结果是:9 pool-1-thread-3
Process finished with exit code 0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
因为之前一直是用的execute方法,最近有个情况需要用到submit方法,所以研究了下。
三个区别:
1、接收的参数不一样
2、submit有返回值,而execute没有
Method submit extends base method Executor.execute by creating and
returning a Future that can be used to cancel execution and/or wait for
completion.
用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。然后我就可以把所有失败的原因综合起来发给调用者。
个人觉得cancel execution这个用处不大,很少有需要去取消执行的。
而最大的用处应该是第二点。
3、submit方便Exception处理
There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this
exception will go to the uncaught exception handler (when you don't
have provided one explicitly, the default one will just print the stack
trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task's return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.
意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。
提交方式和用js的form.submit有什么区别
submit按钮一般出现在网页上需要提交信息到服务器是才使用,一张表单可以有多个普通按钮,且至少有一个提交按钮(需提交表单网页)。Sumit提交表单,Button需要绑定事件才可以用提交数据不可以的,比如说你想实现局部刷新,就不能用Sumit了,用Button绑定事件就好了,如果用Sumit绑定事件的话,在触发事件的同事,也会提交表单的。。。。。。submit需要有表单时,提交时才会带数据。而button默认是不提交任何数据。那么它们的区别就出来了,如果没有表单的话,又想通过提交某些数据给后台进行回应,则需要通过button,当然使用submit也可以,但是前提要拦截onclick事件。当有表单的时候,如果提交的数据很多,那么使用submit比button要好,可以减少很多数据的获取动作。在很多情况下,提交数据是要使用JS进行校验的,但如果这时候用户禁用了JS,那么校验就失效了,如果后台也没有进行校验,那么不合法的数据就进入后台了。通常的做法是通过button提交数据,那么如果用户禁用JS,那么数据提交动作就激活不了,这样能够保证提交到后台的数据是经过前台校验的。Sumit提交表单,Button需要绑定事件才可以用提交数据不可以的,比如说你想实现局部刷新,就不能用Sumit了,用Button绑定事件就好了,如果用Sumit绑定事件的话,在触发事件的同事,也会提交表单的。。。。。。
js提交和submit按钮提交的区别:
1. js提交表单时不会带上 submit 按钮的值(因为没有被单击) 所有浏览器
2. input 回车提交 w3c浏览器会带上submit按钮的值,ie6则不会带
解决办法:增加一个hidden域,用这个来判断,无论用哪种方式提交都会有值
submit按钮上绑定提交事件:
即:
都会带上submit的值, 用js提交都检测不到onsubmit状态
w3c: 提交一次
ie6: 分两次提交,先js在form提交
解决办法:如果按钮为submit则 检测时用onsubmit事件检测
如果按钮为button,则检测通过后在触发submit事件
一定不要用js提交表单,然后又用onsubmit去检测
单纯的用js提交表单, alert, ff下阻塞表单的提交,而其他浏览
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!