Java并发包——使用新的方式创建线程

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个线程之后,将剩下的任务放在了任务队列里,当有任务执行完成之后再将其取出执行。

原文地址:https://www.cnblogs.com/shamao/p/11015558.html