1. 用户请求取消。
2. 有时间限制的操作,如超时设定。
3. 应用程序事件。
4. 错误。
5. 关闭。
如下面这种取消操作实现:/**
* 一个可取消的素数生成器
* 使用volatile类型的域保存取消状态
* 通过循环来检测任务是否取消
*/
@ThreadSafe
public
class
PrimeGenerator
implements
Runnable {
private
final
List<BigInteger> primes =
new
ArrayList<>();
private
volatile
boolean
canceled;
@Override
public
void
run() {
BigInteger p = BigInteger.ONE;
while
(!canceled){
p = p.nextProbablePrime();
synchronized
(
this
) {
//同步添加素数
primes.add(p);
}
}
}
/**
* 取消生成素数
*/
public
void
cancel(){
canceled =
true
;
}
/**
* 同步获取素数
* @return 已经生成的素数
*/
public
synchronized
List<BigInteger> get(){
return
new
ArrayList<>(primes);//避免primes逸出
}
}
public
class
PrimeGeneratorTest {
public
static
void
main(String[] args) {
PrimeGenerator pg =
new
PrimeGenerator();
new
Thread(pg).start();
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
finally
{
pg.cancel();
//取消
}
System.out.println(
"all primes: "
+ pg.get());
}
}
中断
下面通过中断实现取消功能:
/**
* 通过中断来实现取消
* 不采用boolean变量,
* 防止在queue.put()时由于阻塞,不能检查到boolean变量而无法取消
* 但使用interrupt就可以,
*
即使queue.put()阻塞, 也会检查到interrupt信号,从而抛出IntteruptedException
* 从而达到取消的目的
*/
public
class
PrimeProducer
extends
Thread {
private
final
BlockingQueue<BigInteger> queue;
public
PrimeProducer(BlockingQueue<BigInteger> queue){
this
.queue = queue;
}
@Override
public
void
run() {
try
{
BigInteger p = BigInteger.ONE;
while
(!Thread.currentThread().isInterrupted()){//说明1
queue.put(p = p.nextProbablePrime());//说明2
}
}
catch
(InterruptedException e) {
// thread exit
}
}
/**
* 取消
*/
public
void
cancel(){
interrupt();
//中断当前线程
}
}
说明1:
为什么还要判断线程是否被中断呢?因为如果put操作不阻塞的话,即使线程被中断了,也会由于还没有执行到取消点而没有抛出异常,所以线程会一直执行。
说明2:
put操作如果发生阻塞,此时如果调用interrupt中断线程,则会抛出异常,因为阻塞操作包含了取消点,捕获到异常后,可以做一些结束前的工作。
注意:使用中断时,如果有循环,通常都要在循环中判断是否被中断(isInterrupted())。
1. 传递异常。
2. 恢复(保存)中断状态,从而使调用栈的上层代码能够对其进行处理:
如果你不想或者不能传递InterruptedException,则需要用另一种方式保存中断请求,实现这个目的的标准方法是再次调用 interrupt()来恢复中断状态,因为一个中断请求一旦被捕获到之后,也就是说抛出InterruptedException异常之后,则该信号就消失了(线程的中断标识也被清零),所以,如果捕获到中断请求而不想处理也不想直接向外抛出,则需要再一次调用interrupt(),此时上层代码便也会收到一个中断请求。
interrupted 和 isInterrupted方法的区别:
注意,interrupted不是interrupt。
调用interrupt()中断一个线程时,会把该线程的中断标识设为true,调用该线程对象的isInterrupted()方法可以查询该中断标识,只查询不改变,而调用静态方法interrupted()来查询中断标识时,除了返回中断标识,还会把中断标识设为false。
通过Future实现取消
public
void
timedRun(Runnable r,
long
timeout, TimeUnit unit)
throws
InterruptedException {
Future<?> task = taskExec.submit(r);
try
{
task.get(timeout, unit);
}
catch
(ExecutionException e) {
//任务执行中抛出异常
}
catch
(TimeoutException e) {
//任务超时处理
}
finally
{
//如果任务执行完毕,则没有影响; 如果任务执行中,则会中断任务
if
(task !=
null
) task.cancel(
true
);
}
}
1、java.io包中的同步Socket I/O。如套接字中进行读写操作read, write方法,不会响应中断,但是可以通过关闭底层的Socket,让read或write抛出一个SocketException,所以可以用socket.close()来处理阻塞。
2. java.nio包中的同步I/O。如当中断或关闭正在InterruptibleChannel上等待的线程时,会对应抛出ClosedByInterruptException或AsynchronousCloseException。
3. Selector的异步I/O。如果一个线程在调用Selector.select时阻塞了,则不会响应中断(interrupt),但是调用close或 wakeup会使线程抛出ClosedSelectorException。
4、等待获得内部锁时。如Synchronized阻塞时不会响应中断。但Lock类的lockInterruptibly允许在等待锁时响应中断。
/**
* 通过改写interrupt方法将非标准的取消操作封装在Thread中
*/
public
class
ReaderThread
extends
Thread {
private
final
Socket socket;
private
final
InputStream in;
private
int
bufferSize;
public
ReaderThread(Socket socket, InputStream in) {
this
(socket, in,
1024
);
}
public
ReaderThread(Socket socket, InputStream in,
int
bufferSize) {
this
.socket = socket;
this
.in = in;
this
.bufferSize = bufferSize;
}
public
void
interrupt() {
try
{
socket.close();
//中断前关闭socket
}
catch
(IOException e) {
}
finally
{
super
.interrupt();
}
}
@Override
public
void
run() {
try
{
byte
[] buf =
new
byte
[bufferSize];
while
(
true
) {
int
count = in.read(buf);
if
(count <
0
) {
break
;
}
else
if
(count >
0
) {
processBuffer(buf, count);
}
}
}
catch
(IOException e) {
// 线程中断处理
}
}
...
}
/**
* 可取消的任务接口
*/
public
interface
CancellableTask<T>
extends
Callable<T> {
void
cancel();
RunnableFuture<T> newTask();
}
/**
* 使用了Socket的任务
* 在取消时需要关闭Socket
*/
public
abstract
class
SocketUsingTask<T>
implements
CancellableTask<T> {
private
Socket socket;
public
void
setSocket(Socket socket) {
this
.socket = socket;
}
@Override
public
T call()
throws
Exception {
//do working
...
}
@Override
public
synchronized
void
cancel() {
try
{
if
(socket !=
null
){
socket.close();
}
}
catch
(IOException ignored) {
}
}
@Override
public
RunnableFuture<T> newTask() {
return
new
FutureTask<T>(
this
){
@Override
public
boolean
cancel(
boolean
mayInterruptIfRunning) {
try
{
SocketUsingTask.
this
.cancel();
}
catch
(Exception ignored) {
}
return
super
.cancel(mayInterruptIfRunning);
}
};
}
}
/**
* 通过newTaskFor将非标准的取消操作封装在任务中
*/
public
class
CancellingExecutor
extends
ThreadPoolExecutor {
public
CancellingExecutor(
int
corePoolSize,
int
maximumPoolSize,
long
keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super
(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected
<T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
if
(callable
instanceof
CancellableTask){
//若是我们定制的可取消任务
return
((CancellableTask<T>)callable).newTask();
}
return
super
.newTaskFor(callable);
}
}
1、 shutdown: 安全关闭。不再接受新任务提交,待所有队列中的任务执行完成再关闭。这些任务什么时候执行完呢?可以调用 awaitTermination(timeout, unit),该方法会阻塞直到所有任务执行完为止,或者超时,或者当前线程被中断。通常这两个方法结合使用,关闭之后,等待关闭完成。
2、shutdownNow: 强行关闭。不再接受新任务提交,停止正在执行的任务,并返回未开始执行的任务列表,注意:
返回的任务可能并不是提交给ExecutorService的相同对象,它们可能是经过包装的已提交任务的实例,所以,不能简单地用"=="来判断返回的对象是否是提交之前的对象。
/**
* 封装ExecutorService实现日志服务
*/
public
class
LogService2 {
private
final
ExecutorService exec = Executors.newSingleThreadExecutor();
private
final
PrintWriter writer;
public
LogService2(PrintWriter writer){
this
.writer = writer;
}
/**
* 产生日志
* @param msg 日志内容
* @throws InterruptedException
*/
public
void
log(String msg)
throws
InterruptedException{
exec.execute(
new
WriteTask(msg));
}
/**
* 停止日志服务
* @throws InterruptedException
*/
public
void
stop(
long
timeout, TimeUnit unit)
throws
InterruptedException{
try
{
exec.shutdown();
//平缓关闭服务
//关闭服务后, 阻塞到所有任务被执行完毕或者超时发生,或当前线程被中断
exec.awaitTermination(timeout, unit);
}
finally
{
writer.close();
}
}
...
}
/**
* 在ExecutorService中跟踪在关闭之后被取消的任务
*/
public
class
TrackingExecutor
extends
AbstractExecutorService {
private
final
ExecutorService exec;
private
final
Set<Runnable> tasksCancelledAtShutdown =
Collections.synchronizedSet(
new
HashSet<Runnable>());
public
TrackingExecutor(ExecutorService exec) {
this
.exec = exec;
}
/**
* 获取关闭后取消的任务
*/
public
List<Runnable> getCancelledTasks(){
if
(!exec.isTerminated()){
throw
new
IllegalStateException(
"service doesn't stop"
);
}
return
new
ArrayList<>(tasksCancelledAtShutdown);
}
@Override
public
void
execute(
final
Runnable command) {
exec.execute(
new
Runnable() {
@Override
public
void
run() {
try
{
command.run();
}
finally
{
if
(isShutdown() &&
//若Executor已经关闭了
Thread.currentThread().isInterrupted()){
//应该在finally前面加一个catch,然后恢复中断,否则这里中断标识应该会总是false
tasksCancelledAtShutdown.add(command);
}
}
}
});
}
}
/**
* 将异常写入日志的UncaughtExceptionHandler
*/
public
class
UEHLogger
implements
UncaughtExceptionHandler {
@Override
public
void
uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.SEVERE,
"the thread with exceptoin: "
+t.getName(), e);
}
}