1、使用Future和线程池
JDK中关于Future的定义如下
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
/*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
/**
* A {@code Future} represents the result of an asynchronous
* computation. Methods are provided to check if the computation is
* complete, to wait for its completion, and to retrieve the result of
* the computation. The result can only be retrieved using method
* {@code get} when the computation has completed, blocking if
* necessary until it is ready. Cancellation is performed by the
* {@code cancel} method. Additional methods are provided to
* determine if the task completed normally or was cancelled. Once a
* computation has completed, the computation cannot be cancelled.
* If you would like to use a {@code Future} for the sake
* of cancellability but not provide a usable result, you can
* declare types of the form {@code Future<?>} and
* return {@code null} as a result of the underlying task.
*
* <p>
* <b>Sample Usage</b> (Note that the following classes are all
* made-up.)
* <pre> {@code
* interface ArchiveSearcher { String search(String target); }
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
* void showSearch(final String target)
* throws InterruptedException {
* Future<String> future
* = executor.submit(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
* } catch (ExecutionException ex) { cleanup(); return; }
* }
* }}</pre>
*
* The {@link FutureTask} class is an implementation of {@code Future} that
* implements {@code Runnable}, and so may be executed by an {@code Executor}.
* For example, the above construction with {@code submit} could be replaced by:
* <pre> {@code
* FutureTask<String> future =
* new FutureTask<String>(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
* executor.execute(future);}</pre>
*
* <p>Memory consistency effects: Actions taken by the asynchronous computation
* <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a>
* actions following the corresponding {@code Future.get()} in another thread.
*
* @see FutureTask
* @see Executor
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled();
/**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone();
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Cancellation is performed by the {@code cancel} method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled
红色字体可以看出,Future是可以取消的,只要对应的计算任务没有完成。下面是一个建议的例子,可以看到Future和线程池联合使用,即可实现可中断的线程。新定义的线程中封装了JDK的Runnable,对应的成员为task,同时加入类型为Future的cancel成员,当调用自定义线程的start()方法时,将task提交给线程池执行,同时获取返回值赋给cancel。之后可以通过自定义线程的cancel()方法,取消线程的执行,也就是调用成员cancel中的cancel()函数。/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package lpmoon;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
/**
*
* @author lpmoon
*/
public class InterruptableThread {
class MyThread {
Runnable task;
Future cancel;
ScheduledExecutorService stpe = Executors.newSingleThreadScheduledExecutor();
MyThread(Runnable task) {
this.task = task;
}
public void start() {
cancel = stpe.submit(this.task);
if (!stpe.isShutdown()) {
stpe.shutdown();
}
}
public void cancel() {
cancel.cancel(true);
if (!stpe.isShutdown()) {
stpe.shutdown();
}
}
}
public void test() {
Runnable run = new Runnable() {
@Override
public void run() {
try {
System.out.println("Thread start!");
Object synObj = new Object();
synchronized (synObj) {
synObj.wait(10000);
}
System.out.println("Thread end!");
} catch (InterruptedException ex) {
}
}
};
MyThread myThread = new MyThread(run);
myThread.start();
}
public static void main(String[] args) {
InterruptableThread test = new InterruptableThread();
test.test();
}
}
执行上面的代码,在Netbeans中可以得到如下结果,
run:
Thread start!
Thread end!
成功构建 (总时间: 10 秒)
修改test函数,为如下所示
public void test() {
Runnable run = new Runnable() {
@Override
public void run() {
try {
System.out.println("Thread start!");
Object synObj = new Object();
synchronized (synObj) {
synObj.wait(10000);
}
System.out.println("Thread end!");
} catch (InterruptedException ex) {
}
}
};
MyThread myThread = new MyThread(run);
myThread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
myThread.cancel();
}执行上面的代码,在Netbeans中可以得到如下结果,
run:
Thread start!
成功构建 (总时间: 5 秒)
线程在执行的过程中被终止。
Future还有一个特殊的用法,可以实现定时任务的取消。很多人可能遇到过如下情况,使用ThreadPoolExecutor(Executors.newSingleThreadScheduledExecutor()或者Executors.newScheduledThreadPool (corePoolSize))实现定时任务的时候,有时需要终止定时任务,而在某个时间有需要重启定时任务。而在ThreadPoolExecutor中只有shutdown能够停止当前的定时任务,但是在上面的业务场景中意味着线程池需要不停的终止,初始化,造成了极大的资源浪费。而使用Future可以巧妙地解决这个问题。
代码如下所示
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package lpmoon;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*
* @author lpmoon
*/
public class InterruptableThread {
public static void main(String[] args) {
ScheduledExecutorService stse = Executors.newSingleThreadScheduledExecutor();
Future cancel = stse.scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
System.out.println("1");
}
}, 0, 1, TimeUnit.SECONDS);
cancel.cancel(true);
}
}2、使用共享变量
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package lpmoon;
/**
*
* @author lpmoon
*/
public class InterruptableThread implements Runnable{
private volatile boolean cancelled = false;
private int i = 0;
@Override
public void run() {
while(!cancelled){
System.out.println(i++);
}
}
public void cancel(){
cancelled = true;
}
public static void main(String[] args){
InterruptableThread test = new InterruptableThread();
new Thread(test).start();
test.cancel();
}
}
但是,这种方法有一定的限制。适用于那些,需要通过循环不停的往下执行的任务,只有这样才能使标志位起到作用。而且共享变量的使用,需要保证在各个线程之间的可见性。因为cancel函数的调用可能在另外一个线程中,修改cancelled后,由于JMM的特殊性可能无法保证工作线程可以看到该值得改变,导致线程无法立刻终止。
3、使用中断
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package lpmoon;
/**
*
* @author lpmoon
*/
public class InterruptableThread extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("1");
}
}
public void cancel() {
interrupt();
}
public static void main(String[] args) {
InterruptableThread test = new InterruptableThread();
test.start();
test.cancel();
}
}
本文介绍了Java中三种实现线程可中断的方法:1) 使用Future和线程池,通过Future的cancel方法来取消任务;2) 利用共享变量进行中断标志检查;3) 直接调用线程的interrupt方法。通过这些方式,可以在执行过程中终止线程,避免资源浪费。
116

被折叠的 条评论
为什么被折叠?



