学习FutureTask
来源:互联网 发布:淘宝卖家怎样发布宝贝 编辑:程序博客网 时间:2024/05/07 12:03
根据http://beautyboss.farbox.com/post/study/shen-ru-xue-xi-futuretask该博客内容来学习FutureTask,该博客的jdk为1.7,我使用1.8来学习,看看二者是否有所区别。
1.Callable接口
对于需要执行的任务需要实现Callable接口。
可以发现,在jdk1.7和jdk1.8中,Callable接口的定义并未发生变化。均是定义了接口函数call,返回类型为泛型V。
Callable接口和Runnable接口很像,不同的是,Runnable接口没有返回值,也不能抛出异常。下面是Runnable接口。
可以发现,Runnable接口中定义了抽象函数run。这里有个小疑问,接口中的函数默认为abstract的,这里又明确使用了关键字abstract。不知道是为什么,加上这个关键字当然也不会报错,可能是历史遗留问题。
2.Future接口
Future的接口定义如下:
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. Ifthe 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 {@codetrue}.
*
* @param mayInterruptIfRunning {@code true} if the threadexecuting this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not becancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(booleanmayInterruptIfRunning);
/**
* Returns {@code true} if this task was cancelled before itcompleted
* normally.
*
* @return {@code true} if this task was cancelled beforeit 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 wasinterrupted
* while waiting
*/
V get() throwsInterruptedException, 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 wasinterrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以看出,在jdk1.8中,Future接口的定义还是保持以前的样子。
1)cancel()
该方法尝试取消任务的执行。如果任务已经结束或者已经被取消了,或者由于一些原因不能被取消,则会返回false。如果任务没有被执行,则函数返回true,且该任务不再会被执行。如果任务已经在执行中,则需要由mayInterruptIfRunning来决定,加入该参数为true,则中断任务执行并返回true。若该参数为false,则不中断任务执行。
当该方法返回后,后续调用isDone()则肯定会返回true。该方法返回ture时,后续调用isCancelled()则会返回true。
2)isCanceled()
如果任务在结束前(正常执行结束或者执行异常结束)被取消,则返回true。
3)isDone()
任务完成后返回ture,包括:正常结束、抛出异常、或者任务被取消。这些情况都会返回true。
4)get()
获取任务执行结果,如果任务未执行完,则会阻塞等待任务执行完成并返回执行结果。如果任务取消,则抛出CancellationException异常;如果
任务抛出异常,则抛出ExecutionException异常;如果阻塞过程中被中断,则抛出InterruptedException异常。
5)get(longtimeout,Timeunit unit)
带超时时间版本的get。timeout:最大等待时间;unit:时间单位。抛出异常与get函数相同,只不过多了一个TimeoutException,当wait超时后抛出该异常。
3.FutureTask
FutureTask实现RunnableFuture接口,RunnableFuture接口继承了Runnable和Future接口。FutureTask可以用来创建对象。所以FutureTask可以被当成一个Runnable直接被Thread执行,也可以作为Future来接收callable接口的计算结果。
public class FutureTask<V> implementsRunnableFuture<V> {...}
public interface RunnableFuture<V>extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
4.使用
FutureTask一般可以通过executorservice来使用,也可以直接通过thread来使用。
package test.com.test.lpf;
import java.util.concurrent.*;
/**
*Created by Administrator on 2017/5/19.
*/
public class Hello {
public static void main(String args[]) throws ExecutionException,InterruptedException{
/**
* 第一种方式Future +ExecutorService
* Task task = new Task();
* ExecutorService service =Executors.newCachedThreadPool();
* Future<Integer> future =service.submit(task);
* service.shutdown();
*/
/**
* 第二种方式FutureTask + ExecutorService
* ExecutorService executor =Executors.newCachedThreadPool();
* Task task = new Task();
* FutureTask<Integer>futureTask = new FutureTask<Integer>(task);
* executor.submit(futureTask);
*/
/**
* 第三种方式FutureTask + thread
*
*/
//新建FutureTask,需要一个实现了Callable接口的类的实例作为构造函数参数
FutureTask<Integer> futureTask = new FutureTask<Integer>(newTask());
//新建thread对象并启动
Thread thread = new Thread(futureTask);
thread.setName("Task Thread");
thread.start();
try{
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Thread [" + Thread.currentThread().getName()+ "] is running");
//调用isDone()判断任务是否结束
if(!futureTask.isDone()){
System.out.println("Task is not done");
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
int result = 0;
try{
result = futureTask.get();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("result is " + result);
if(futureTask.isDone()){
System.out.println("Task is done");
}
}
static class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception{
System.out.println("Thread [" + Thread.currentThread().getName()+ "] is runnning");
int result = 0;
for(int i = 0; i < 100; i++){
result += i;
}
Thread.sleep(3000);
return result;
}
}
}
5.FutureTask
关于FutureTask,FutureTask有两个构造函数:
public FutureTask(Callable<V>callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; //ensure visibility of callable
}
这个构造函数会把传入的Callable对象保存到this字段中,该字段定义为:private Callable<V> callable;用来保存底层的调用
/** The underlying callable; nulled outafter running */
private Callable<V> callable;
在被执行完后会指向Null。
接下来初始化state为NEW,该字段用来保存FutureTask的内部的任务执行状态,一共有7种状态。
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING =5;
private static final int INTERRUPTED = 6;
可以发现,state声明为Volatile。也就是说任意一个线程更改了该变量,其它所有的线程都会知道该最新值。
NEW:表示是个新的任务或是没有被执行完的任务,这是初始状态。
COMPLETING:任务已经执行完或者任务执行发生异常,但是任务执行结果或者异常原因还没有保存到outcome字段(outcome字段用来保存任务执行结果或者异常原因)的时候,状态会从NEW变更到COMPLETING,但是持续时间比较短,属于中间状态。
NORMAL:任务已经执行完成并且任务执行结果保存到了outcome字段,状态会从COMPLETING变为NORMAL,这是一个最终态。
EXCEPTIONAL:任务执行发生异常,并且异常原因保存到outcome字段,状态会从COMPLETING变为EXCEPTIONAL,这是一个最终态。
CANCELLED:任务还未执行或者已经开始执行但是还未执行完成的时候,用户调用了cancel(false)方法取消任务且不中断任务执行线
程,状态会从NEW转为CANCELLED,这是一个最终态。
INTERRUPTING:任务还未执行或者已经开始执行但是还未执行完成的时候,用户调用了cancel(true)方法取消任务并且要中断任务执行线程但是还没有中断任务执行线程之前,状态会从NEW转为INTERRUPTING,这是一个中间态。
INTERRUPTED:调用interrupt()中断任务执行线程之后,状态会从INTERRUPTING变为INTERRUPTED,这是一个最终态。
注意:所有值大于COMPLETING的状态都表示任务已经执行完成(任务正常执行完成,任务异常,或者任务被取消)。
另外一个构造函数:
public FutureTask(Runnable runnable, V result){
this.callable = Executors.callable(runnable, result);
this.state = NEW; //ensure visibility of callable
}
这个构造函数会把传入的Runnable封装成一个Callable对象保存在this.callable字段中,同时任务执行的结果会保存到result中,
加入不需要保存结果,则传入一个null。
接下来看一下Executoes.callable方法:
public static <T> Callable<T>callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
可以看到这里主要是调用RunnableAdapter来适配。
static final class RunnableAdapter<T>implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
该适配器实现了Callable接口,在call实现中调用Runnable.run(),把传入的result作为任务结果返回。
构造函数介绍完毕,接下来就是new一个FutureTask对象,在另一个线程中执行该任务,无论是直接使用thread还是线程池的办法,
实际上都是执行的run()方法。
6.run()
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null,Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}finally {
// runner must be non-null untilstate is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
1)run方法首先会判断状态是否为NEW,不是NEW则说明任务已经执行过或者被取消,直接返回。
2)若状态为NEW,则通过UNSAFE类把任务执行线程引用CAS的保存在runner字段中,如果保存失败,则直接返回。
查看compareAndSwapObject:
public final native booleancompareAndSwapObject(Object var1, long var2, Object var4, Object var5);
a)该方法声明为native的,简单来讲,就是java通过该函数来调用非java代码。在定义一个native方法的时候,并不提供实现,因为实现是在外部由非java代码来完成的。
b)声明为final是为了防止该方法被重写(因为native方法可以使用被子类重写)。
c)如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会
被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并
不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。
d)CompareAnd Swap(CAS)
简单的说就是比较并交换,CAS操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。
现在考虑该函数UNSAFE.compareAndSwapObject(this, runnerOffset,
null,Thread.currentThread()))
传入的参数为this,runnerOffset,null,Thread.currentThread()。也就是说,判断this+runnerOffset的位置是否为null,假如为null,则将当前线程更新过去。假如不为null,则不做任何操作。
这里面涉及了runnerOffset:
private staticfinal longstateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
UNSAFE =sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
stateOffset =UNSAFE.objectFieldOffset
(k.getDeclaredField("state"));
runnerOffset =UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
waitersOffset =UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));
} catch(Exception e) {
throw new Error(e);
}
}
可以看到,在FutureTask这个构造函数中,有静态代码块,静态代码块我们都知道,会在类第一次使用时初始化,且只初始化一次。在该静态代码块中,有runnerOffset的赋值操作。使用了UNSAFE类的objectFileldOffset以及class的getDeclaredField方法。其中getDeclaredField方法我们都知道,属于java反射机制范畴的。通过getDeclaredField(runner)获取运行的线程。
/** The thread running the callable; CASed during run() */private volatile Thread runner;
所以,综上所述:通过getDeclaredField(runner)获得运行callable的线程,通过objectFileldOffse获得偏移量,通过当前对象+偏移量(this+runnerOffset)来判断该位置是否为null,假如为null,则通过Thread.currentThread()将当前运行线程赋值过去。
再看UNSAFE.compareAndSwapObject(this, runnerOffset,
null,Thread.currentThread()))
假如值被更改,该函数返回true,未更改则返回false。
所以if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
这个判断语句就好理解了,就是将任务线程保存在runner中。假如已经保存过,则直接返回。
3)接下来声明了Callable对象,调用call方法来执行任务。
4)假如任务执行异常,则调用setException()方法来保存异常信息。
protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); }}
在setException()中:
a) 首先将state从NEW变为COMPLETING。
b) 将outcome字段赋值为异常原因。(正如前面所说,outcome字段保存任务执行结果或者异常原因)
c) CAS将state从COMPLETING改变为EXCEPTIONAL。(前面状态关键字中介绍)。
d) 调用finishCompletion()。
5)如果任务执行成功,则调用set方法来设置执行结果。
protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); }}
在set()中:
a) 将state从NEW改变为COMPLETING。
b) 将执行结果保存到outcome字段中。
c) CAS将state改变为NORMAL。
d) 调用finishCompletion()。
6)最后
a)将runner设置为null。(在state状态被设置前,runner必须为non-null来防止并发调用run()。UNSAFE.compareAndSwapObject可以用来防止并发调用)。
b)判断state的值是否大于等于INTERRUPTING,根据判断来调用handlePossibleCancellationInterrupt()。
private void handlePossibleCancellationInterrupt(int s) { // It is possible for our interrupter to stall before getting a // chance to interrupt us. Let's spin-wait patiently. if (s == INTERRUPTING) while (state == INTERRUPTING) Thread.yield(); // wait out pending interrupt
假如该线程被打断,处于INTERRUPTING状态,则调用yield。yield()将使当前线程从执行态变为可执行态,也就是就绪态吧。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次不会执行到了。
发起任务线程跟执行任务线程通常情况下都不会是同一个线程,在任务执行线程执行任务的时候,任务发起线程可以查看任务执行状态、获取任务执行结果、取消任务等等操作,接下来分析下这些操作。
1) get
任务发起线程可以调用get()方法来获取任务执行结果,如果此时任务已经执行完毕则会直接返回任务结果,如果任务还没执行完毕,则调用方会阻塞直到任务执行结束返回结果为止。get()方法实现如下:
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s);}
a) 判断当前的state <= COMPLETING是否成立。前面说过,COMPLETING状态是任务执行完成的临界状态。
b) 如果成立,表明任务还没有结束(这里的结束包括任务正常执行完毕,任务执行异常,任务被取消),则会调用awaitDone()进行阻塞等待。
c) 如果不成立表明任务已经结束,调用report()返回结果。
2)awaitDone
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
//计算等待截止时间 final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) {
//判断阻塞线程是否被中断,如果被中断则在等待队
// 列中删除该节点并抛出InterruptedException异常 if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); }//获取当前状态,如果状态大于COMPLETING,说明任务已经结束(要么异常结束,要么正常结束,要么取消),则把thread显式置空,返回结果。 int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; }
//如果状态处于中间状态COMPLETING
// 表示任务已经结束但是任务执行线程还没来得及给outcome赋值
// 这个时候让出执行权让其他线程优先执行 else if (s == COMPLETING) // cannot time out yet Thread.yield();
//如果等待节点为空,则构造一个等待节点 else if (q == null) q = new WaitNode();
//如果还没有入队列,则把当前节点加入waiters首节点并替换原来waiters else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
//如果需要等待特定时间,则先计算要等待的时间
// 如果已经超时,则删除对应节点并返回对应的状态 else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; }
// 6. 阻塞等待特定时间 LockSupport.parkNanos(this, nanos); }
// 6. 阻塞等待直到被其他线程唤醒 else LockSupport.park(this); }}
awaitDone()中有个死循环,每一次循环都会
- 判断调用get()的线程是否被其他线程中断,如果是的话则在等待队列中删除对应节点然后抛出InterruptedException异常。
- 获取任务当前状态,如果当前任务状态大于COMPLETING则表示任务执行完成,则把thread字段置null并返回结果。
- 如果任务处于COMPLETING状态,则表示任务已经处理完成(正常执行完成或者执行出现异常),但是执行结果或者异常原因还没有保存到outcome字段中。这个时候调用线程让出执行权让其他线程优先执行。
- 如果等待节点为空,则构造一个等待节点WaitNode。
- 如果第四步中新建的节点还没如队列,则CAS的把该节点加入waiters队列的首节点。
- 阻塞等待。
假设当前state=NEW且waiters为NULL,也就是说还没有任何一个线程调用get()获取执行结果,这个时候有两个线程threadA和threadB先后调用get()来获取执行结果。再假设这两个线程在加入阻塞队列进行阻塞等待之前任务都没有执行完成且threadA和threadB都没有被中断的情况下(因为如果threadA和threadB在进行阻塞等待结果之前任务就执行完成或线程本身被中断的话,awaitDone()就执行结束返回了),执行过程是这样的,以threadA为例:
第一轮for循环,执行的逻辑是q == null,所以这时候会新建一个节点q。第一轮循环结束。
第二轮for循环,执行的逻辑是!queue,这个时候会把第一轮循环中生成的节点的netx指针指向waiters,然后CAS的把节点q替换waiters。也就是把新生成的节点添加到waiters链表的首节点。如果替换成功,queued=true。第二轮循环结束。
第三轮for循环,进行阻塞等待。要么阻塞特定时间,要么一直阻塞直到被其他线程唤醒。
在threadA和threadB都阻塞等待之后的waiters结果如图
3.cancel
用户可以调用cancel(boolean)方法取消任务的执行,cancel()实现如下,在jdk1.8中的实现与1.7稍有不同:
public boolean cancel(boolean mayInterruptIfRunning) {
//如果state为NEW且使用CAS来根据是否可以被打断更改state为INTERRUPTING或者CANCELLED if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception
//. 如果需要中断任务执行线程,则低啊用interrupt中断任务执行线程 if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt(); } finally { // final state
//修改state状态为INTERRUPED UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); } } } finally { finishCompletion(); } return true;}
cancel()方法会做下面几件事:
- 判断任务当前执行状态,如果任务状态不为NEW,则说明任务或者已经执行完成,或者执行异常,不能被取消,直接返回false表示执行失败。
- 根据mayInterruptIfRunning的值将state置为INTERRUPTING 或者 CANCELLED,若为INTERRUPTING,则中断任务执行线程。然后将state置为INTERRUPTED。
- 调用finishCompletion。
4.finishCompletion
根据前面的分析,不管是任务执行异常或者正常执行,或者是取消任务,最后都会调用finishCompletion函数。
private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } done(); callable = null; // to reduce footprint}
这个方法的实现比较简单,依次遍历waiters链表,唤醒节点中的线程,然后把callable置空。
被唤醒的线程会各自从awaitDone()方法中的LockSupport.park*()阻塞中返回,然后会进行新一轮的循环。在新一轮的循环中会返回执行结果(或者更确切的说是返回任务的状态)。
5.report
@SuppressWarnings("unchecked")private V report(int s) throws ExecutionException { Object x = outcome;
//任务正常执行完成,返回任务执行结果 if (s == NORMAL) return (V)x;
//任务被取消,抛出CancellationException异常 if (s >= CANCELLED) throw new CancellationException();
//其他状态,抛出执行异常ExecutionException throw new ExecutionException((Throwable)x);}
report()方法用在get()方法中,作用是把不同的任务状态映射成任务执行结果。
映射关系如下图所示:
如果任务处于NEW、COMPLETING和INTERRUPTING这三种状态的时候是执行不到report()方法的,所以没必要对这三种状态进行转换。
6.get(long,TimeUnit)
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; if (s <= COMPLETING &&
//如果awaitDone()超时返回之后任务还没结束,则抛出异常 (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) throw new TimeoutException(); return report(s);}
跟get()不同点在于get(long,TimeUnit)会在awaitDone()超时返回之后抛出TimeoutException异常.
7.isCancelled()和isDone()
这两个方法分别用来判断任务是否被取消和任务是否执行完成,实现都比较简单,代码如下:
public boolean isCancelled() { return state >= CANCELLED;}public boolean isDone() { return state != NEW;}
总结:
总结下,其实FutureTask的实现还是比较简单的,当用户实现Callable()接口定义好任务之后,把任务交给其他线程进行执行。FutureTask内部维护一个任务状态,任何操作都是围绕着这个状态进行,并随时更新任务状态。任务发起者调用get*()获取执行结果的时候,如果任务还没有执行完毕,则会把自己放入阻塞队列中然后进行阻塞等待。当任务执行完成之后,任务执行线程会依次唤醒阻塞等待的线程。调用cancel()取消任务的时候也只是简单的修改任务状态,如果需要中断任务执行线程的话则调用Thread.interrupt()中断任务执行线程。
最后:
有个值得关注的问题就是当任务还在执行的时候用户调用cancel(true)方法能否真正让任务停止执行呢?
在前面的分析中我们直到,当调用cancel(true)方法的时候,实际执行还是Thread.interrupt()方法,而interrupt()方法只是设置中断标志位,如果被中断的线程处于sleep()、wait()或者join()逻辑中则会抛出InterruptedException异常。
因此结论是:cancel(true)并不一定能够停止正在执行的异步任务。
- 学习FutureTask
- 学习 FutureTask
- FutureTask源码学习
- FutureTask深入学习
- 深入学习 FutureTask
- 深入学习 FutureTask
- Android Callable Future FutureTask学习
- FutureTask
- FutureTask
- FutureTask
- FutureTask
- FutureTask
- FutureTask
- FutureTask
- FutureTask
- Future,FutureTask,Executors,ExecutorService,Callable学习笔记
- 疯狂Java学习笔记(66)-----------Callable、Future和FutureTask
- java 中间件学习4-CountDownLatch、CyclicBarrier、Future和FutureTask
- ES5对数组增强的9个API
- win10 uwp 获得元素绝对坐标
- 当前不会命中中断。还没有为该文档加载任何符号。
- iOS学习之打包项目测试的ipa详细版本以及安装测试ipa流程
- 欢迎使用CSDN-markdown编辑器
- 学习FutureTask
- 识别Win10系统两种方法
- 非常完善的Log4net详细说明
- JavaBean特点和使用方法(该文章摘自他人)
- linux--wget
- 网络梳理
- QT学习笔记 -->接受一帧数据并不是一次接受完,分多次接受
- echarts统计图表与工具关系可视化
- 微信小程序开发(十一)五星好评