[size=large]实现一个在规定的时间内完成一个任务,使用线程池实现,如果在规定的时间内,没完成,即结束该线程任务。以下是我的代码:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class My {
private static ExecutorService executor = [b]Executors.newCachedThreadPool()[/b];//定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
Future future = executor.submit(new MyJob());//将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);//设定在200毫秒的时间内完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");//抛出超时异[size=xx-small][/size][size=large][/size]常时打印
}
}
}
//业务job
import java.util.concurrent.Callable;
//实现Callable接口
public class MyJob implements [b]Callable[/b] {
@Override
public Boolean call() {
//do job here
long startTime = System.currentTimeMillis();
for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
执行void函数时输出结果如下:
Time is out
continue...
这和我的要求不符,我原本是希望在抛出异常之后任务即结束,不再执行System.out.println("continue...");
请问如何解决[/size]
[quote]在这个job中的call方法应为有for()循环可以找到在哪中断线程,但是如果只是在连接数据库的话,那就无法知道执行到哪,应该加入中断线程的代码,这应该在外面控制,而不应该在job里面实现中断[/quote]
线程的中断在JDK1.0版本中提供了stop()方法,但这个方法极度不安全,会不管
当前对象的状态而直接释放所有的monitor,结束线程,会导致对象处于错误的状态。
所以已经不被推荐使用。
试想,有个任务,A向B转账10000¥,需要先从A账户上扣款10000¥,再将B账号加
10000¥,如果A账号扣款完,这是从外面强行杀死线程,会导致什么后果?
中止一个线程最好的方式就是使用中断,在外面通知线程中断,线程将中断状态设置
为true,而具体是否要中断,中断时需要如何处理以保证数据状态是正确的,这些
应交给任务自己处理。
在什么地方控制中断,应该任务中控制,由你的业务逻辑决定,就算是
操作数据库也一样可以设置中断点,或者说更应该判断中断点,来决定是否回滚
事务。
这好像没什么好办法
future.get(200, TimeUnit.MILLISECONDS)这个只是等结果的时间
在外面控制时间,200毫秒后,执行
future.cancel(true);
[quote]
catch (TimeoutException e) {
System.out.println("Time is out");//抛出超时异[size=xx-small][/size]常时打印
}
[/quote]
在你的System.out.print()语句下,添加一行代码:
[code="java"]
future.cancel(true);
[/code]
看看行不行
在for循环后面抛个异常这样挺好哈
if (System.currentTimeMillis() - startTime >=2000)
throw new TimeoutException();
或者在你的job里实现取消策略,在需要时自己取消。
Future future = executor.submit(new MyJob());// 将任务提交到线程池中
executor.shutdown();//没人提交了该shutdown()了吧
[code="java"]
public class My {
private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
// future.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
executor.shutdown();//出现异常即可终止任务。
}
executor.shutdown();
System.out.println(result);
}
}
[/code]
需要调用 executor.shutdown();
终止当前线程任务。
[code="java"]
catch (TimeoutException e) {
System.out.println("Time is out");//抛出超时异常时打印
boolean res = future.cancel(true);
System.out.println("res:" + res);
}
[/code]
你这么改一下,看看 res 的值是true 还是 false;
[code="java"]
public class My {
private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
public static void main(String[] args) {
Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
executor.shutdown(); //关闭线程池 不允许再提交任务,并不是关闭线程池中的线程
try {
future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
future.cancel(true);//关闭超时的这个任务
//executor.shutdownNow(); //因为这个任务超时,终止了所有的线程.
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
}
}
/**
*
* 任务。其实完全可以或应该在任务重控制自己何时超时,
* 当然我顺着你的思路来了
*/
public static class MyJob implements Callable<Boolean> {
public Boolean call() throws Exception {
long startTime = System.currentTimeMillis();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
return false; //这里要响应future的中断啊,
//返回false吧,或者超时了抛个异常也行,这就是所谓的中断策略
//Java5以后的线程终止方法被废了,很多时候终止策略应该自己来实现
}
System.out.println("continue...");
return true;
}
}
}
[/code]
我验证过了在"time is out"的打印后面加future.cancel(true);是可行的,不知道你怎么验证的?
} catch (TimeoutException e) {
System.out.println("Time is out");//抛出超时异[size=xx-small][/size]常时打印
future.cancel(true);
}
[quote]res为true[/quote]
呃,那这就奇怪了啊。明明任务已经被取消了,怎么还会打印出下面那句话呢。郁闷啊。。。
[quote]是不是使用线程池提交,线程池将任务分配给多个线程处理了,cancel()后仍然将任务分配给其他的线程执行?[/quote]这个需要看源码啊。但我认为,应该是针对每一个被submit上去的任务开一个线程来执行
你能把你的for循环里面模仿超时的语句贴出来吗?我用的是Thread.sleep();
[quote]使用thread能实现吗?还要有返回值的 [/quote]
使用 Thread.sleep() 的话,你可以这么写:
[code="java"]
try{
Thread.sleep(2000);
} catch (InterruptedException e) {
return false;
}
System.out.println("continue...");
return true;
[/code]
主要是要实现自己的终止策略
比如在for循环中可以加入这样的判断
[code="java"]
for(;;){
if(Thread.currentThread().isInterrupted())
return false; //这就是来响应future.cancel啊,你不响应他,咋停啊
}
[/code]
Java 并发编程实践这本书,看了基本上可以解决大部分问题了
[quote]public class MyJob implements Callable {
@Override
public Boolean call() {
//do job here
long startTime = System.currentTimeMillis();
for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
} [/quote]
你这个测试任务写的有问题,根本没有控制中断,for循环会一直执行到结束。
要在循环中加入线程中断的判断。
Thread.currentThread().interrupted()
[quote]public class MyJob implements Callable {
@Override
public Boolean call() {
//do job here
long startTime = System.currentTimeMillis();
for(int i=0;System.currentTimeMillis()-startTime<2000;i++){
if(Thread.currentThread().interrupted()){
return false;
}
}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
} [/quote]
在外面执行future.cancel(true)这时就能结束任务了。
我习惯的做法是在工作线程内部设置一个boolean变量,如果外界想让线程结束就设置一下这个变量。在线程内部会判断这个变量,看是否要退出。
一个这种的办法是获取MyJob当前的线程;在MyJob对象中声明一个变量thread
[code="java"]import java.util.concurrent.Callable;
//实现Callable接口
public class MyJob implements Callable {
[b] public Thread thread = null;[/b]
@Override
public Boolean call() throws Exception {
//do job here
long startTime = System.currentTimeMillis();
[b]thread = Thread.currentThread();[/b]//获取当前线程 for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}[/code]
在My对象中当超时,就对该工作任务的线程进行终止。
[code="java"]Boolean result = false;
[b]MyJob job = new MyJob();[/b] Future future = executor.submit(job);// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
// future.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
[b]job.thread.stop();[/b] }[/code]
[quote]我也想用stop()[/quote]
stop() 已经是不推荐使用了,就算你获得了JOB所在的线程,请也别使用这个方法
[quote]
例子很好,不过要在任务中控制,不知道该如何在任务中设置中断点
比如说简单的获取数据库connection,我要在规定时间内判断,若没连接成功就中断线程
[/quote]
因为你获得不到这个JOB所在的线程,所以你没有办法来中断它的。
[quote]
难道就无解了吗?
[/quote]
暂时没想出来,晚上回去我有开发环境了,我再试试看
一般涉及到网络连接的地方,都会提供超时接口,getConnection也不例外,
不需要自己来控制超时。
[code="java"]
/**
* Sets the maximum time in seconds that a driver will wait
* while attempting to connect to a database.
*
* @param seconds the login time limit in seconds; zero means there is no limit
* @see #getLoginTimeout
*/
public static void setLoginTimeout(int seconds) {
loginTimeout = seconds;
}
[/code]
[quote]xiaoyang0601 写道
一个这种的办法是获取MyJob当前的线程;在MyJob对象中声明一个变量thread
Java代码
import java.util.concurrent.Callable;
//实现Callable接口
public class MyJob implements Callable {
public Thread thread = null; //定义一个当前任务线程
@Override
public Boolean call() throws Exception {
//do job here
long startTime = System.currentTimeMillis();
thread = Thread.currentThread();//获取当前线程,,外界可以访问
for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
import java.util.concurrent.Callable;
//实现Callable接口
public class MyJob implements Callable {
[b] public Thread thread = null;[/b]
@Override
public Boolean call() throws Exception {
//do job here
long startTime = System.currentTimeMillis();
[b]thread = Thread.currentThread();[/b]//获取当前线程 for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
在My对象中当超时,就对该工作任务的线程进行终止。
Java代码
Boolean result = false;
MyJob job = new MyJob();//将提交的任务
Future future = executor.submit(job);// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
// future.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
job.thread.stop();//访问MyJob线程。
}
Boolean result = false;
[b]MyJob job = new MyJob();[/b] Future future = executor.submit(job);// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
// future.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
[b]job.thread.stop();[/b] }
我也想用stop(),但是job.thread取不到该线程啊
[/quote]
能访问到啊。你仔细看看代码。在MyJob中声明一个任务线程,这个任务线程就是当前任务的,在My对象中,声明一个MyJob job = new MyJob();
Future future = executor.submit(job);//
在超时异常代码中
job.thread.stop();是可以访问的。你或许访问修饰符有误。
还有你提的这个问题,中超时不再处理以后的任务,但这个超时的后的任务放在任务中,欠妥,可以提取出来,进行重构,先进性必要的超时处理,如果没有超时在My对象中进行处理业务,如果超时就不再处理。
[code="java"]package dl.java.iteye;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.Callable;
public class My {
private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
MyJob job = new MyJob();
Future future = executor.submit(job);// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
job.thread.stop();
}
}
//业务job
//实现Callable接口
static class MyJob implements Callable<Boolean> {
public Thread thread = null;
@Override
public Boolean call() throws Exception {
//do job here
long startTime = System.currentTimeMillis(); //获取当前运行线程
thread = Thread.currentThread();
for(int i=0;System.currentTimeMillis()-startTime<2000;i++){}//模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
}
[/code]
这是一个案例,你根据实际情况,修改吧。你可以复制,运行一下。打印结果:
Time is out
改了一下你原先的代码
[code="java"]
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.Callable;
public class My {
// 业务job
// 实现Callable接口
static class MyJob implements Callable<Boolean> {
public Boolean call() {
// do job here
long startTime = System.currentTimeMillis();
for (int i = 0; System.currentTimeMillis() - startTime < 2000; i++) {
//测试当前线程是否已经中断,配合future.cancel(true)
if (Thread.interrupted())
return false;
}// 模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
try {
result = future.get(200, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {// 超时异常
System.out.println("Time is out");
future.cancel(true);// 中断执行此任务的线程
}
}
} [/code]
测试通过,不会打印"continue..."
你对自己要做什么可能很清楚,但你对线程池能做什么确实很模糊,自己把自己绕晕了!!!
回滚事务,如果超时事务回滚到操作以前的场景!
楼主cancle地方不对,中断异常后返回也有问题,仔细看下面:
[code="java"]
public class Test {
private static ExecutorService executor = Executors.newCachedThreadPool();// 定义一个线程池
/**
* @param args
*/
public static void main(String[] args) {
Boolean result = false;
Future<Boolean> future = executor.submit(new MyJob());// 将任务提交到线程池中
try {
result = future.get(20, TimeUnit.MILLISECONDS);// 设定在200毫秒的时间内完成
// future.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("Time is out");// 抛出超时异[size=xx-small][/size]常时打印
//executor.shutdown();// 出现异常即可终止任务。
[color=red]future.cancel(true);[/color]
}
//executor.shutdown();
System.out.println(result);
}
}
class MyJob implements Callable {
@SuppressWarnings("static-access")
@Override
public Boolean call() {
// do job here
// long startTime = System.currentTimeMillis();
// for (int i = 0; System.currentTimeMillis() - startTime < 2000; i++) {
// System.out.println(i);
// }
try {
Thread.currentThread().sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
[color=red]return false; [/color]
}
// 模拟业务执行超过2000毫秒,即已经超时
System.out.println("continue...");
return true;
}
}
[/code]
String N= "";
String M= "";
String id= "";
String pwd = "";
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
this.M = req.getParameter("M").trim();
this.N= req.getParameter("N").trim();
this.id = req.getParameter("id").trim();
this.pwd = req.getParameter("pwd").trim();
/................../
}
当多个系统同时调用该servlet的时候,出现了servlet的非线程安全的问题。比如有两个系统同时调用该servlet,代码中的N、M、id、pwd的值会出现,一个线程的数据被另一个线程的数据“覆盖”的问题。
[color=red] 我的问题是,第一:是否跟这几个变量是全部变量有关,如果设置为局部变量会出现这个问题吗?第二:使用多个servlet能解决这个问题,但是是最好的办法吗?[/color]