0.8、多线程学习——FutureTask

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好。

FutrueTask

一般而言,我们总是强调可以通过 FutrueTask 来获得线程执行的结果,这是其区分 Thread和Runnable的一个亮点。但是其实FutureTask本身既不包含具体执行的代码,也无法触发线程的执行,其只是作为得到线程结果的一个“代表”,此外,它还有一个特性就是可以增加了线程的运行状态并且可以取消线程执行——虽然线程可能已经执行结束了导致取消未必成功。

为 FutrueTask 增加自定义代码逻辑

FutureTask 并不包含具体的代码逻辑,具体的代码逻辑需要你继承 Callable 接口

实现 Callable 接口并对象传入 FutureTask 构造函数

我们需要实现 Callable 接口,并且重写其中的 call()方法,然后将重写的对象作为一个入参传入到 FutureTask 的构造函数,这样我们就得到了一个具有代码逻辑的 FutureTask 对象。
代码如下:

 //callable 是 future 的具体实现内容
 Callable callable=new Callable() {
      @Override
      public Object call() throws Exception {
          System.out.println(Thread.currentThread().getName());
          return true;
      }
  };
  //将 callable 传入到 FutureTask 构造函数
  FutureTask future=new FutureTask(callable);
FutureTask 的启动

我们无法直接通过 FutureTask 来启动这个线程,因为FutureTask并没有提供 start()方法。
我们可以借助于 Thread 类,或者线程池来执行 FutureTask 中Callable 中的run方法——在一个线程中执行。

使用 Thread.start() 执行 FutureTask

将 FutureTask 对象作为入参传入Thread的构造函数,然后通过Thread.start()方法 运行FutureTask.
显然这里运用了装饰器模式,最底层为 Callable对象,FutureTask 使其获得了获取执行结果的能力,Thread 使FutureTask 获得了执行的能力。
看代码吧

 //callable 是 future 的具体实现内容
 Callable callable=new Callable() {
      @Override
      public Object call() throws Exception {
          System.out.println(Thread.currentThread().getName());
          return true;
      }
  };
  
 //将 callable 传入到 FutureTask 构造函数
 FutureTask futureTask=new FutureTask(callable);
 //将futureTask 对象传入到 Thread 构造函数
 Thread t1=new Thread(futureTask);
 t1.setName("thread1");
 //运行线程
 t1.start();
使用线程池执行 FutureTask

线程池执行线程任务直接提交就可以了,但是不同线程池在执行FutureTask 所展现的特性是不一样的,这个在下面的 FutureTask 的阻塞特性中进行介绍,这里只是提供一个线程池执行FutureTask的代码样例。

线程池和 FutureTask 类

在使用线程池执行FutureTask 时,我们有两种方式,第一种类似于 Thread中,先声明 Callable 对象,然后将之传入到 FutureTask 对象,然后直接提交给线程池,类似于下面

  //callable 是 future 的具体实现内容
  Callable callable=new Callable() {
       @Override
       public Object call() throws Exception {
           System.out.println(Thread.currentThread().getName());
           return true;
       }
   };
   //将 callable 传入到 FutureTask 构造函数
   FutureTask futureTask=new FutureTask(callable);

   ExecutorService e=Executors.newFixedThreadPool(1);
   e.submit(futureTask);
线程池和 Future 接口

第二种是,我们可以借助于 Future 接口

//callable 是 future 的具体实现内容
Callable callable=new Callable() {
     @Override
     public Object call() throws Exception {
         System.out.println(Thread.currentThread().getName());
         return true;
     }
 };
//线程池
 ExecutorService e=Executors.newFixedThreadPool(1);
 Future f=e.submit(callable);
 

FutureTask.get()

对于 Futrue.get() 也是一样的

FutureTask.get() 用于获取线程执行结果

FutureTask.get() 用于获取线程执行结果,对于 Futrue.get() 也是一样的。
其返回类型是在 Callable的call方法中决定的,当然你可以返回一个null

FutureTask.get() 会阻塞当前线程

当调用 FutureTask.get() 或者 Futrue.get()时会阻塞当前线程。
如果你使用了线程池,会展示不同的特性——某种情况下其会阻塞线程池。
比如在线程池执行一个 FutureTask.get() ,对于 newFixedThreadPool,其至少会阻塞一个线程资源,对于 newCachedThreadPool,则会阻塞整个线程池,这个和线程池的阻塞队列有关,newCachedThreadPool的阻塞队列是 SynchronousQueue,其只有当下一个线程出现时才会释放当前线程,而 FutureTask.get() 阻塞了这个过程。

FutureTask.get(n) 可以设置最大等待时间

这个不多说,设置最大等待时间。

 //将 callable 传入到 FutureTask 构造函数
        FutureTask futureTask = new FutureTask(callable);
       
      	//···略

        //获取结果-这里注意时间
        List listResult = null;
        try {
            listResult = (List) futureTask.get(2, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
        	//防止系统意外,取消 futureTask
            futureTask.cancel(true);
        }

FutureTask.cancel(boolean)

FutureTask 允许我们取消线程执行,而且,允许取消正在运行的线程——以抛出异常的方式中断
FutureTask 有很多个状态,我们可以查看它的状态

 /**初始状态*/
 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;
上代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Future 相关的测试
 * @author jie.wu
 */
public class FutureTest {

	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		Callable c=new Callable<String>() {
			@Override
			public String call() throws Exception {
				System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"开始:"+System.currentTimeMillis());
				Thread.sleep(5*1000);
				return "Over:"+System.currentTimeMillis();
			}
		
		};
		final FutureTask f=new FutureTask(c);
		Thread t=new Thread(f);
		t.setName("thread1");
		t.start();
		
		//在另一个线程异步获取结果-避免阻塞当前线程
		Thread thread2=new Thread("thread2") {

			@Override
			public void run() {
				super.run();
				try {
					System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"等待结果:"+f.get());
				} catch (InterruptedException | ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		};
		thread2.start();
		
		System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"线程状态-是否完成:"+f.isDone());
		System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"线程状态-开始取消-可否执行:"+f.cancel(true));
		System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"线程状态-是否已经在执行-不等于New:"+f.isDone());
		System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"线程状态-是否取消:"+f.isCancelled());
		
	}
	
}
执行结果
1565695517794thread1开始:1565695517794
1565695517794main线程状态-是否完成:false
1565695517794main线程状态-开始取消-可否执行:true
1565695517795main线程状态-是否已经在执行-不等于New:true
1565695517795main线程状态-是否取消:true
Exception in thread "thread2" java.util.concurrent.CancellationException
	at java.util.concurrent.FutureTask.report(FutureTask.java:121)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at del.FutureTest$2.run(FutureTest.java:41)
如果我们注释掉中断futuer的一行
//System.out.println(System.currentTimeMillis()+Thread.currentThread().getName()+"线程状态-开始取消-可否执行:"+f.cancel(true));
执行结果
1565695530740thread1开始:1565695530740
1565695530741main线程状态-是否完成:false
1565695530741main线程状态-是否已经在执行-不等于New:false
1565695530741main线程状态-是否取消:false
1565695530741thread2等待结果:Over:1565695535744

提供一个工具类

https://blog.csdn.net/zsx_xiaoxin/article/details/123898171

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.function.Supplier;

/**
 * @Title: Utils
 * @Description: FutureUtils
 * @Updatedby:
 */
@UtilityClass
@Slf4j
@UtilityClass
@Slf4j
public class FutureUtils {
    public <T> Future<T> getFuture(ExecutorService executor, Supplier<T> supplier) {
        return CompletableFuture.supplyAsync(supplier, executor);
    }

    public static <T> T getFromFuture(Future<T> future, T defaultT, int timeoutMillis) {
        if (future == null) {
            return defaultT;
        }
        try {
            return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.warn("func=getFromFuture default={}", defaultT, e);
            return defaultT;
        }
    }

    public static <T> T getFromFutureDefaultSupplier(Future<T> future, Supplier<T> defaultT, int timeoutMillis) {
        if (future == null) {
            return defaultT.get();
        }
        try {
            return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.warn("func=getFromFuture default={}", defaultT, e);
            return defaultT.get();
        }
    }

    public static void voidFuture(ExecutorService executor, Runnable runnable) {
        CompletableFuture.runAsync(runnable, executor);
    }

    public static <T> T getFromFuture(Future<T> future, T defaultT, int timeoutMillis, String errorMsg) {
        if (future == null) {
            return defaultT;
        }
        try {
            return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("func=getFromFuture default={}, errorMsg={}", defaultT, errorMsg, e);
            return defaultT;
        }
    }
}

用法举例


ExecutorService executor = Executors.newFixedThreadPool(3);

    public boolean su(Supplier<Boolean> s){
        return s.get();
    }
    public boolean queryTimeOut(){
        try {
            Thread.sleep(2000);
            System.out.println("运行了");
        } catch (InterruptedException e) {
            System.out.println("超时了");
        }
        return true;
    }
    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        System.out.println(begin);
        Future<Boolean> future = FutureUtils.getFuture(executor,()->queryTimeOut());
        //以默认值兜底 future 超时
        boolean result = FutureUtils.getFromFuture(future,true,1000);
        //以函数兜底 future 超时
        result = FutureUtils.getFromFutureDefaultSupplier(future, (Supplier<Boolean>) ()->queryTimeOut(),1000);
        System.out.println(result);
        long end = System.currentTimeMillis();
        System.out.println(end-begin);
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值