基础 | 并发编程 - [Callable & FutureTask]

@

§1 开线程方式

  • 继承 Thread

    new Thread(()->{
       // do
    },"A").start();
    
  • 实现 Runable

    new Thread(()->{
       // do
    },"A").start();
    
  • 实现 Callable

    class Job implements Runnable{
        @Override
        public void run() {
    		// do
        }
    }
    

    与 Runable 的区别
    实现 call() 而不是 run()
    有返回值
    会抛出异常

  • 实现 线程池

§2 Callable & FutureTask 使用

public class CallableJob implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("=========");
        return "used";
    }

    public static void main(String[] args) {
        new Thread(new FutureTask<String>(new CallableJob())).start();
    }
}

FutureTask 提供了 Callable 到 Runable 的适配(适配器模式
通过此方式解决通过开启线程调用 Callable.call() 的目的
具体方式为:
实现了 Runable 接口,并且提供一个接受 Callable 的构造方法

//public interface RunnableFuture<V> extends Runnable, Future<V> 
public class FutureTask<V> implements RunnableFuture<V> {
	public FutureTask(Callable<V> callable) {
	    if (callable == null)
	        throw new NullPointerException();
	    this.callable = callable;
	    this.state = NEW; 
	}
	//FutureTask 中的 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();// 在 run() 中调用 Callable 的 call()
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
			// ...
        }
    }
}

应用场景
相对于 Runable,Callable 提供一个返回值
并在结合 FutureTask 后,可以稍后获取到这个返回值
常用于分支-合并模式
即,在分步骤业务中,另开线程处理复杂或耗时的环节,并最终汇总结果

public class CallableJob implements Callable<Integer> {
    @Override
    public Integer call()  {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100 * 1000;
    }

    public static Integer smallJob(Integer... eles){
        return Arrays.stream(eles).mapToInt(Integer::intValue).sum();
    }

    public static void main(String[] args) {
        FutureTask<Integer> bigJob = new FutureTask<Integer>(new CallableJob());
        try {
            new Thread(bigJob).start();
            int result = smallJob(1,2,3,4,5);
            result += bigJob.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

注意

  • FutureTask.get() 时,会 阻塞线程
  • 可以通过设置超时时间进行控制,时间超出后会抛出 TimeoutException
  • 也可是使用 FutureTask.isDone() 循环等待完成,但依然阻塞
while (!bigJob.isDone()){
    System.out.println("working");
    TimeUnit.MILLISECONDS.sleep(500);
}
result += bigJob.get();
  • 多个线程争抢一个 FutureTask 时,只会有一个线程成功,FutureTask 只会执行一次\
posted @ 2025-05-20 14:30  问仙长何方蓬莱  阅读(12)  评论(0)    收藏  举报