多线程:Callable, Future, FutureTask和线程池

版权声明:转载请注明出处 https://blog.csdn.net/baidu_25104885/article/details/89297169

Callable和Runnable

Runnable封装一个异步运行的任务,没有返回值。

  • Callable与Runnable类似,但有返回值。
  • Callable的call()方法可以抛出异常
  • 运行Callable可以拿到Future对象
public interface Callable<V> 
{
	V call() throws Exception;
}

类型参数是返回值的类型。Callable< Integer>返回Integer对象

Future和FutureTask

Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。
Future对象的所有者在结果计算好后可以获得它。

public interface Future<V> {
	V get() throws InterruptedException, ExecutionException;
	V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
	void cancel(boolean mayInterruptIfRunning);
	boolean isCancelled();
	boolean isDone();
}
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,抛出TimeoutException异常。
  • cancel()取消该计算。如果计算没开始,计算被取消返回true;正在运行中,若mayInterruptIfRunning为true,则被中断返回true;若已结束,返回false。
  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  • isDone方法表示任务是否已经完成,若任务完成,则返回true。

Future提供了三种功能:

  • 判断任务是否完成;
  • 能够中断任务;
  • 能够获取任务执行结果。

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
  FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。
  另:多个线程执行一个FutureTask只会执行一次,其它属于复用,多次计算应用多个FutureTask

public FutureTask(Callable<V> callable) {  
     if (callable == null)  
          throw new NullPointerException();  
     sync = new Sync(callable);  
} 
public FutureTask(Runnable runnable, V result) {  
     sync = new Sync(Executors.callable(runnable, result));  
}  

FutureTask可将Callable转换成Future和Runnable。
例如: 计算匹配文件数目

public class FutureTest{
	public static void main(String[] args) {
		try (Scanner in = new Scanner(System.in)){
			System.out.print("Enter base directory (e.g. /user/local/jdk5.0/src): ");
			String directory = in.nextLine();
			System.out.print("Enter keyword (e.g. volatile): ");
			String keyword = in.nextLine();
			// 用MatchCounter创建一个FutureTask对象,并用来启动一个线程
			MatchCounter counter = new MatchCounter(new File(directory), keyword);
			FutureTask<Integer> task = new FutureTask<>(counter); 
			Thread t = new Thread(task); // 作为一个Runnable
			t.start();
			try{
			    // 每一个对get()调用都会发生阻塞直到获得结果
				System.out.println(task.get() + " matching files.");  // 作为一个Future
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		} catch (InterruptedException e) {
		}
	}
}
class MatchCounter implements Callable<Integer> {
	private File directory;
	private String keyword;
	public MatchCounter(File directory, String keyword) {
		this.directory = directory;
		this.keyword = keyword;
	}
	public Integer call() {
		int count = 0;
		try {
			File[] files = directory.listFiles();
			List<Future<Integer>> results = new ArrayList<>();
			
			for (File file:files) {
				if (file.isDirectory()) {
				    // 递归,对于每个子目录都产生一个新的MatchCounter并为它启动一个线程
					MatchCounter counter = new MatchCounter(file, keyword);
					FutureTask<Integer> task = new FutureTask<>(counter);
					// 将FutureTask对象放在ArrayList<Future<Integer>>中
					results.add(task);
					Thread t = new Thread(task);
					t.start();
				} else {
					if (search(file)) count++;
				}
            }
            
            for (Future<Integer>  result:results) {
            	try {
            		count += result.get();
            	} catch (ExecutionException e) {
            		e.printStackTrace();
            	}
            }
		} catch (InterruptedException e) {
		}
		return count;
    }
	
	// 查找给定文件中是否包含关键词
	public boolean search(File file) {
		try {
			try (Scanner in = new Scanner(file, "UTF-8")) {
				boolean found = false;
				while (!found && in.hasNextLine()) {
					String line = in.nextLine();
					if (line.contains(keyword)) found = true;
				}
				return found;
			} catch (IOException e) {
				return false;
			}
		}
	}
}

Callable、Future和线程池

  1. Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;

  2. ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;

    • execute(task) 异步执行tasks,无返回值
    • submit(task) 异步执行,且带task返回值,可通过task.get 实现同步到主线程
    • invoke(task) 有join, tasks会被同步到主进程
  3. 抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;

  4. 类ThreadPoolExecutor继承了类AbstractExecutorService。

public ThreadPoolExecutor( int corePoolSize,    //核心线程的数量
                          int maximumPoolSize,    //最大线程数量
                          long keepAliveTime,    //超出核心线程数量以外的线程空余存活时间
                          TimeUnit unit,    //存活时间的单位
                          BlockingQueue<Runnable> workQueue,    //保存待执行任务的队列
                          ThreadFactory threadFactory,    //创建新线程使用的工厂
                          RejectedExecutionHandler handler // 当任务无法执行时的处理器
                          ) {...}

可以使用两个方法向线程池提交任务,分别为execute()和submit()方法
execute()方法用于提交不需要返回值的任务(Runnable),所以无法判断任务是否被线程池执行成功。
submit()方法用于提交需要返回值的任务(Callable)。线程池会返回一个future类型的对象

例1

package multiplethread;
   
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
   
public class TestThread {
   
    public static void main(String[] args) throws InterruptedException {
           
        ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
           
        threadPool.execute(new Runnable(){
   
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("任务1");
            }
               
        });
   
    }
   
}

例2

public class FutureTest{
	public static void main(String[] args) {
		try (Scanner in = new Scanner(System.in)){
			System.out.print("Enter base directory (e.g. /user/local/jdk5.0/src): ");
			String directory = in.nextLine();
			System.out.print("Enter keyword (e.g. volatile): ");
			String keyword = in.nextLine();
			
			ExecutorService pool = new Executors.newCachedThreadPool();
			
			MatchCounter counter = new MatchCounter(new File(directory), keyword, pool); // 参数pool用于递归创建MatchCounter加入线程池
			Future<Integer> result = pool.submit(counter); 
			
			try {
				System.out.println(result.get() + " matching files.");
			} catch (ExecutionException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
			}
			pool.shutdown();
            // 强制转换为ThreadPoolExecutor查看线程池在执行器生命周期中的最大尺寸
            int largestPoolSize = ((ThreadPoolExecutor) pool).getLargestPoolSize();
            System.out.println("largest pool size = " + largestPoolSize);
		} 
	}
}
class MatchCounter implements Callable<Integer> {
	private File directory;
	private String keyword;
	private ExecutorService pool;
	private int count;
	public MatchCounter(File directory, String keyword, ExecutorService pool) {
		this.directory = directory;
		this.keyword = keyword;
		this.pool = pool;
	}
	public Integer call() {
		count = 0;
		try {
			File[] files = directory.listFiles();
			List<Future<Integer>> results = new ArrayList<>();
			
			for (File file:files) {
				if (file.isDirectory()) {
				    // 递归,对于每个子目录都产生一个新的MatchCounter并加入线程池
					MatchCounter counter = new MatchCounter(file, keyword, pool);
					Future<Integer> result = pool.submit(counter);
			        // 保存返回的Future对象
					results.add(result);
				} else {
					if (search(file)) count++;
				}
            }
            
            for (Future<Integer>  result:results) {
            	try {
            		count += result.get();
            	} catch (ExecutionException e) {
            		e.printStackTrace();
            	}
            }
		} catch (InterruptedException e) {
		}
		return count;
    }
	
	// 查找给定文件中是否包含关键词
	public boolean search(File file) {
		try {
			try (Scanner in = new Scanner(file, "UTF-8")) {
				boolean found = false;
				while (!found && in.hasNextLine()) {
					String line = in.nextLine();
					if (line.contains(keyword)) found = true;
				}
				return found;
			} catch (IOException e) {
				return false;
			}
		}
	}
}

Ref:

  1. https://www.cnblogs.com/dolphin0520/p/3932921.html
  2. http://how2j.cn/k/thread/thread-threadpool/357.html#nowhere

猜你喜欢

转载自blog.csdn.net/baidu_25104885/article/details/89297169