Java并发包——使用新的方式创建线程
摘要:本文主要学习了如何使用Java并发包中的类创建线程。
使用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 实际上是通过 public
使用线程池创建线程
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个线程之后,将剩下的任务放在了任务队列里,当有任务执行完成之后再将其取出执行。