先上demo:
package callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class callableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call");
return 111;
}
}
运行结果:
call
111
为什么只输出一次结果呢?
分析:
从FutureTask()的源码部分得知,一开始就将this.callable=callable;this.state=NEW;
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask()是Runnable的子类,所以肯定重写了run()方法,查看源码:
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, 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 until state 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);
}
}
第一个if判断是为了return的,暂时不管,继续往下看,发现只有当(callable!=null&&state==NEW),才会boolean ran/有可能执行call(),刚刚说了这俩条件都满足了,就接着往下走,发现 if (ran) set(result);,那就接着看set()的源码:
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
ok,他把state改成了normal,乘胜追击,继续看finishCompletion()的源码:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (WAITERS.weakCompareAndSet(this, 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
}
最后一行:callable = null; ,很明显,call()的2个执行条件全部都不满足了。
那就能解释了:💖A线程和B线程的FutureTask是同一个💖,都调用了class MyThread,当第一个线程执行时会改变callable和state,导致满足了run()的return,就不会执行后面的call()了。
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
......
论坛上的解决办法:
①new 两个FutureTask()
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask1 = new FutureTask<>(myThread);
FutureTask<Integer> futureTask2 = new FutureTask<>(myThread);
new Thread(futureTask1,"A").start();
new Thread(futureTask2,"B").start();
Integer integer1 = futureTask1.get();
Integer integer2 = futureTask2.get();
System.out.println(integer1);
System.out.println(integer2);
②修改callable和state。但是如下图源码所示,callable和state都是私有的,无法直接获取
private volatile int state;
private Callable<V> callable;
那怎么办呢?反射
这里借用坛友的写法:
Field callable = task.getClass().getDeclaredField("callable"); //反射获得callable
callable.setAccessible(true); //解除访问限制
Object call = callable.get(task);
System.out.println(task.get());
Field state = task.getClass().getDeclaredField("state"); //反射获得state
state.setAccessible(true);
state.set(task,0); //设置state值
callable.set(task,call); //设置callable值
new Thread(task).start();
System.out.println(task.get());
为什么state.set(task,0); :
private volatile int state;
private static final int NEW = 0; //刚创建,0就是new
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;
补充:官方这样设计是为了提高效率——缓存返回值,下次可以直接把值返回出去,不用计算