Java - 线程池 + Runnable、Callable、FutureTask

ExecutorService

五种线程池的适应场景

  • newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。
  • newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。
  • newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。
  • newScheduledThreadPool:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。
  • newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行

详解:ExecutorService 线程池详解

 

Runnable、Callable、FutureTask

Java - 线程池 + Runnable、Callable、FutureTask_第1张图片

 相同点:都属于线程池中要被运行的任务;

 

不同点:

Runnable是无返回值的任务,可以在线程中使用

Callable是有返回值的任务 ,不可以在线程中使用

FutureTask是有返回值,而且更易于管理和控制的任务,不可以在线程中使用;

 

前两者通过查看他们类可以很清楚的知道:

public interface Runnable {
   /**这个任务运行完之后没有返回值*/
    public abstract void run();
}
-----------------------------------------------
public interface Callable {
   /**这个任务运行完之后返回泛型 V*/
    V call() throws Exception;
}

而FutureTask稍微复杂一点,其实看看它的类结构:

Runnable

public class MyRunnable implements Runnable {
	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}
	}
 
}

----------------------------------------
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/*
 * 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
 * 
 * 如何实现线程的代码呢?
 * 		A:创建一个线程池对象,控制要创建几个线程对象。
 * 			public static ExecutorService newFixedThreadPool(int nThreads)
 * 		B:这种线程池的线程可以执行:
 * 			可以执行Runnable对象或者Callable对象代表的线程
 * 			做一个类实现Runnable接口。
 * 		C:调用如下方法即可
 * 			Future submit(Runnable task)
 *			 Future submit(Callable task)
 *		D:我就要结束,可以吗?
 *			可以。
 */
public class ExecutorsDemo {
	public static void main(String[] args) {
		// 创建一个线程池对象,控制要创建几个线程对象。
		// public static ExecutorService newFixedThreadPool(int nThreads)
		ExecutorService pool = Executors.newFixedThreadPool(2);
 
		// 可以执行Runnable对象或者Callable对象代表的线程
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
 
		//结束线程池
		pool.shutdown();
	}
}

Callable

  Callable+Future:

Future接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操

    class MyCallable implements Callable {
        private String taskNum;

        MyCallable(String taskNum) {
            this.taskNum = taskNum;
        }

        public Object call() throws Exception {
            System.out.println(">>>" + taskNum + "任务启动");
            Date dateTmp1 = new Date();
            Thread.sleep(1000);
            Date dateTmp2 = new Date();
            long time = dateTmp2.getTime() - dateTmp1.getTime();
            System.out.println(">>>" + taskNum + "任务终止");
            return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
        }
    } 
----------------------------------------------------------------
    public static void main(String[] args) throws ExecutionException,
            InterruptedException {
        System.out.println("----程序开始运行----");
        Date date1 = new Date();
        // 创建一个线程池  
        ExecutorService pool = Executors.newFixedThreadPool(5);
        // 创建多个有返回值的任务  
        List list = new ArrayList();
        for (int i = 0; i < taskSize; i++) {
            Callable c = new MyCallable(i + " ");
            // 执行任务并获取Future对象  
            Future f = pool.submit(c);
            // System.out.println(">>>" + f.get().toString());  
            list.add(f);
        }
        // 关闭线程池  
        pool.shutdown();

        // 获取所有并发任务的运行结果  
        for (Future f : list) {
            // 从Future对象上获取任务的返回值,并输出到控制台  
            System.out.println(">>>" + f.get().toString());
        }

        Date date2 = new Date();
        System.out.println("----程序结束运行----,程序运行时间【"
                + (date2.getTime() - date1.getTime()) + "毫秒】");
    }
} 

使用JDK中Executors.newFixedThreadPool方法创建ExecutorService,ExecutorService的submit方法接收Callable接口的实现,JDK内部将弄成线程处理,使用Future接收submit方法的返回值,当future调用get方法时,如果线程还没有执行完,程序阻塞在这里,直到线程执行完。

FutureTask

        Future VS FutureTask

        使用Future时,我们需要实现Callable接口,并通过ExecutorService接口的submit方法获取返回的Future对象。

        使用FutureTask时,根据FutureTask的构造函数可以看到FutureTask既可以接收Callable的实现类,也可以接收Runnable的实现类。当你传入的是Callable的实现类时,可以获取线程执行的结果;传入Runnable的实现类时,由于Runnable的实现没有返回值,需要传入一个你设置的线程完成标识,也就是result,然后当线程结束时会把你传入的result原值返回给你。

我们先来看看FutureTask的实现

    public class FutureTask implements RunnableFuture {  

FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:

    public interface RunnableFuture extends Runnable, Future {  
        void run();  
    }  

分析:FutureTask除了实现了Future接口外还实现了Runnable接口(即可以通过Runnable接口实现线程,也可以通过Future取得线程执行完后的结果),因此FutureTask也可以直接提交给Executor执行。
最后我们给出FutureTask的两种构造函数:

    public FutureTask(Callable callable) {  
    }  
    public FutureTask(Runnable runnable, V result) {  
    }  

 

Callable+FutureTask:

    package com.zejian.Executor;  
    import java.util.concurrent.ExecutorService;  
    import java.util.concurrent.Executors;  
    import java.util.concurrent.Future;  
    import java.util.concurrent.FutureTask;  
    /** 
     * @author zejian 
     * @time 2016年3月15日 下午2:05:43 
     * @decrition callable执行测试类 
     */  
    public class CallableTest {  
          
        public static void main(String[] args) {  
    //      //创建线程池  
    //      ExecutorService es = Executors.newSingleThreadExecutor();  
    //      //创建Callable对象任务  
    //      CallableDemo calTask=new CallableDemo();  
    //      //提交任务并获取执行结果  
    //      Future future =es.submit(calTask);  
    //      //关闭线程池  
    //      es.shutdown();  
              
            //创建线程池  
            ExecutorService es = Executors.newSingleThreadExecutor();  
            //创建Callable对象任务  
            CallableDemo calTask=new CallableDemo();  
            //创建FutureTask  
            FutureTask futureTask=new FutureTask<>(calTask);  
            //执行任务  
            es.submit(futureTask);  
            //关闭线程池  
            es.shutdown();  
            try {  
                Thread.sleep(2000);  
            System.out.println("主线程在执行其他任务");  
              
            if(futureTask.get()!=null){  
                //输出获取到的结果  
                System.out.println("futureTask.get()-->"+futureTask.get());  
            }else{  
                //输出获取到的结果  
                System.out.println("futureTask.get()未获取到结果");  
            }  
              
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            System.out.println("主线程在执行完成");  
        }  
    } 

执行结果:

Callable子线程开始计算啦!
主线程在执行其他任务
Callable子线程计算结束!
futureTask.get()-->12497500
主线程在执行完成

也可

Callable+Future:参考上面Callable的内容

你可能感兴趣的:(Java,开发)