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

  • 相关阅读:
    Neither BindingResult nor plain target object for bean name 'command' available as request attribute
    a href 相对路径 与绝对路径
    sql with as用法详解
    union和union all的区别
    从此不再惧怕URI编码:JavaScript及C# URI编码详解
    Server.UrlEncode、HttpUtility.UrlDecode的区别
    两种获取connectionString的方式
    微软SQLHelper.cs类 中文版
    web.config connectionStrings 数据库连接字符串的解释
    SQL Server 事务处理 回滚事务
  • 原文地址:https://www.cnblogs.com/shamao/p/11015558.html
Copyright © 2020-2023  润新知