Java并发包——使用新的方式创建线程
摘要:本文主要学习了如何使用Java并发包中的类创建线程。
部分内容来自以下博客:
https://www.cnblogs.com/dolphin0520/p/3949310.html
使用Callable接口创建线程
Callable与Runnable
之前学习多线程的时候,使用java.lang包下的Runnable接口可以创建线程。
1 @FunctionalInterface 2 public interface Runnable { 3 public abstract void run(); 4 }
发现由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
Callable位于java.util.concurrent包下,它是一个函数式接口,在它里面声明了一个方法,只不过这个方法叫做call()。
1 @FunctionalInterface 2 public interface Callable<V> { 3 V call() throws Exception; 4 }
通过源码可以看到call()方法是一个泛型接口,可以返回V类型的数据,并且支持抛出异常。
Future
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future类位于java.util.concurrent包下,它是一个接口:
1 public interface Future<V> { 2 // 用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。 3 // 参数mayInterruptIfRunning表示是否允许取消正在执行的任务,如果设置true,则表示可以取消正在执行过程中的任务。 4 // 如果任务已经完成,返回false。 5 // 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false。 6 // 如果任务还没有执行,返回true。 7 boolean cancel(boolean mayInterruptIfRunning); 8 9 // 表示正在执行的任务是否被取消成功,如果在完成前被取消成功,返回true。 10 boolean isCancelled(); 11 12 // 表示任务是否已经完成,若任务完成,则返回true。 13 boolean isDone(); 14 15 // 用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回。 16 V get() throws InterruptedException, ExecutionException; 17 18 // 用来获取执行结果,如果在指定时间内,还没获取到结果,就抛出TimeoutException异常。 19 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; 20 }
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask
我们先来看一下FutureTask的实现:
1 public class FutureTask<V> implements RunnableFuture<V>
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
1 public interface RunnableFuture<V> extends Runnable, Future<V>
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
public FutureTask(Callable<V> callable);
public FutureTask(Runnable runnable, V result);
创建线程并使用
代码如下:
1 public class Demo { 2 public static void main(String[] args) { 3 FutureTask<Integer> futureTask = new FutureTask<Integer>(new NewFutureTask()); 4 new Thread(futureTask).start(); 5 try { 6 Integer i = futureTask.get(); 7 System.out.println(i); 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } catch (ExecutionException e) { 11 e.printStackTrace(); 12 } 13 } 14 } 15 16 class NewFutureTask implements Callable<Integer> { 17 @Override 18 public Integer call() throws Exception { 19 System.out.println("call() ..."); 20 return 100; 21 } 22 }
运行结果如下:
1 call() ... 2 100
因为FutureTask实现了RunnableFuture接口,而RunnableFuture又继承了Runnable和Future接口,所以FutureTask可以看作是Runnable的一个实现类。
所以在创建线程的时候,代码 new Thread(futureTask).start(); 实际上是通过 public Thread(Runnable target) 方法创建的线程。
使用线程池创建线程
Executor
Executor接口是线程池的顶层接口,ExecutorService接口是Executor的子接口,而ThreadPoolExecutor类实现了ExecutorService接口,是线程池的核心类。
Executors类时线程池的一个工具类,里面提供了创建线程池的几个静态方法。
下面的代码展示了使用Executors类的newFixedThreadPool()方法创建一个固定长度的线程池,并向线程池中插入任务的操作:
1 public class Demo { 2 public static void main(String[] args) { 3 ExecutorService threadPool = Executors.newFixedThreadPool(3); 4 5 for (int i = 1; i <= 11; i++) { 6 DemoThread dt = new DemoThread(i); 7 threadPool.submit(dt); 8 } 9 threadPool.shutdown(); 10 } 11 } 12 13 class DemoThread implements Runnable { 14 int taskNo = 0; 15 16 public DemoThread(int taskNo) { 17 this.taskNo = taskNo; 18 } 19 20 @SuppressWarnings("static-access") 21 public void run() { 22 try { 23 System.out.println("task " + taskNo); 24 Thread.currentThread().sleep(4000); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 } 29 }
结果如下:
1 task 1 2 task 3 3 task 2// 此处有等待。 4 task 4 5 task 5 6 task 6// 此处有等待。 7 task 7 8 task 9 9 task 8// 此处有等待。 10 task 10 11 task 11
结果说明:
可以看到因为设置的线程数为3,所以在创建了3个线程之后,将剩下的任务放在了任务队列里,当有任务执行完成之后再将其取出执行。