创建线程四种方法

Thread类详解

Thread类是Java语言的一个重要的基础类,位于java.lang包中。Thread类有不少非常重要的属性和方法,用于存储和操作线程的描述信息

1.线程ID

属性:private long tid,此属性用于保存线程的ID。这是一个private类型的属性,外部只能使用getId()方法访问线程的ID。方法:public long getId(),获取线程ID,线程ID由JVM进行管理,在进程内唯一。

2.线程名称属性:

private String name,该属性保存一个Thread线程实例的名字。

方法一:public final String getName(),获取线程名称。

方法二:public final void setName(String name),设置线程名称。

方法三:Thread(String threadName),通过此构造方法给线程设置一个定制化的名字。

3.线程优先级属性:

private int priority,保存一个Thread线程实例的优先级。

方法一:public final int getPriority(),获取线程优先级。

方法二:public final void setPriority(int priority),设置线程优先级。

4.是否为守护线程

属性:private boolean daemon=false,该属性保存Thread线程实例的守护状态,默认为false,表示是普通的用户线程,而不是守护线程。

方法:public final void setDaemon(boolean on),将线程实例标记为守护线程或用户线程,如果参数值为true,那么将线程实例标记为守护线程

什么是守护线程呢

守护线程是在进程运行时提供某种后台服务的线程,比如垃圾回收(GC)线程。

5.线程的状态

属性:private int threadStatus,该属性以整数的形式保存线程的状态。

方法:public Thread.State getState(),返回表示当前线程的执行状态,为新建、就绪、运行、阻塞、结束等状态中的一种。

Thread的内部静态枚举类State用于定义Java线程的所有状态,具体如下:

         public static enum State {
             NEW,                           //新建
             RUNNABLE,              //就绪、运行
             BLOCKED,               //阻塞
             WAITING,               //等待
             TIMED_WAITING,   //计时等待
             TERMINATED;            //结束
     }

就绪状态和运行状态在内部用同一种状态RUNNABLE表示。就绪状态表示线程具备运行条件,正在等待获取CPU时间片;运行状态表示线程已经获取了CPU时间片,CPU正在执行线程代码逻辑。

6.线程的启动和运行

方法一:public void start(),用来启动一个线程,当调用start()方法后,JVM才会开启一个新的线程来执行用户定义的线程代码逻辑,在这个过程中会为相应的线程分配需要的资源。

方法二:public void run(),作为线程代码逻辑的入口方法。run()方法不是由用户程序来调用的,当调用start()方法启动一个线程之后,只要线程获得了CPU执行时间,便进入run()方法体去执行具体的用户线程代码。

总之,这两个方法非常重要,start()方法用于线程的启动,run()方法作为用户代码逻辑的执行入口。

7.取得当前线程

方法:public static Thread currentThread(),该方法是一个非常重要的静态方法,用于获取当前线程的Thread实例对象。什么是当前线程呢?就是当前在CPU上执行的线程。在没有其他的途径获取当前线程的实例对象的时候,可以通过Thread.currentThread()静态方法获取。

创建线程

继承Thread类创建线程类

(1)需要继承Thread类,创建一个新的线程类。

(2)同时重写run()方法,将需要并发执行的业务代码编写在run()方法中。

     static class DemoThread extends Thread {  //① 
             public DemoThread() {
                 super("DemoThread-" + threadNo++); //②
             }
             ...
      }

实现Runnable接口创建线程目标类

     package java.lang;
     public class Thread implements Runnable {
             ...
             private Runnable target; //执行目标
             public void run() {
                 if(this.target != null) {
                         this.target.run();  //调用执行目标的run()方法
                 }
             }
             public Thread(Runnable target) {  //包含执行目标的构造器
                 init(null, target, "Thread-" + nextThreadNum(), 0);
             }
     }

在Thread类的run()方法中,如果target(执行目标)不为空,就执行target属性的run()方法。而target属性是Thread类的一个实例属性,并且target属性的类型为Runnable。

其中有多个构造器可以为target属性赋值,这些构造器包括如下两个:

(1)public Thread(Runnable target)

(2)public Thread(Runnable target,String name)

Runnable接口
     package java.lang;
     @FunctionalInterface
     public interface Runnable {
         void run();
     }

通过实现Runnable接口创建线程类

具体步骤如下:(1)定义一个新类实现Runnable接口。

(2)实现Runnable接口中的run()抽象方法,将线程代码逻辑存放在该run()实现版本中。

(3)通过Thread类创建线程对象,将Runnable实例作为实际参数传递给Thread类的构造器,由Thread构造器将该Runnable实例赋值给自己的target执行目标属性。

(4)调用Thread实例的start()方法启动线程。(

5)线程启动之后,线程的run()方法将被JVM执行,该run()方法将调用target属性的run()方法,从而完成Runnable实现类中业务代码逻辑的并发执行。

     package com.crazymakercircle.multithread.basic.create;
     // 省略import
     public class CreateDemo2
     {
         public static final int MAX_TURN = 5;
         static int threadNo = 1;
         static class RunTarget implements Runnable  //①实现Runnable接口
         {
             public void run()  //②在这里编写业务逻辑
             {
                 for (int j = 1; j < MAX_TURN; j++)
                 {
                     Print.cfo(ThreadUtil.getCurThreadName() + ", 轮次:" + j);
                 }
                 Print.cfo(getCurThreadName() + " 运行结束.");
             }
         }
     
         public static void main(String args[]) throws InterruptedException
         {
             Thread thread = null;
             for (int i = 0; i < 2; i++)
             {
                 Runnable target = new RunTarget();
                 //通过Thread 类创建线程对象,将Runnable实例作为实际参数传入
                 thread = new Thread(target, "RunnableThread" + threadNo++);
                 thread.start();
             }
         }
     }

实例中静态内部类RunTarget执行目标类,不再是继承Thread线程类,而是实现Runnable接口,需要异步并发执行的代码逻辑被编写在它的run()方法中。

run()方法实现版本中在获取当前线程的名称时,所用的方法是在外部类ThreadUtil中定义的getCurThreadName()静态方法,而不是Thread类的getName()实例方法。原因是:这个RunTarget内部类和Thread类不再是继承关系,无法直接调用Thread类的任何实例方法。

通过实现Runnable接口的方式创建的执行目标类,如果需要访问线程的任何属性和方法,必须通过Thread.currentThread()获取当前的线程对象,通过当前线程对象间接访问。

     public static String getCurThreadName() {
         return Thread.currentThread().getName();  // 获取线程名称
     }

通过继承Thread类的方式创建的线程类,可以在子类中直接调用Thread父类的方法访问当前线程的名称、状态等信息。这也是使用Runnable实现异步执行与继承Thread方法实现异步执行不同的地方。

完成了Runnable的实现类后,需要调用Thread类的构造器创建线程,并将Runnable实现类的实例作为实参传入。可以调用的构造器(即构造函数)包括如下三个:

(1)public Thread(Runnable target)

(2)public Thread(Runnable target,String name)

(3)public Thread(ThreadGroup group,Runnable target)

若调用以上第二个构造器构造线程时可以指定线程的名称,则实例如下:

     thread = new Thread(new RunTarget(), "name" + threadNo++);

线程对象创建完成后,调用Thread线程实例的start()方法启动新线程的并发执行。这时,Runnable实例的run()方法会在新线程Thread的实例方法run()中被调用。

创建Runnable线程目标类的两种方式

(1)通过匿名类优雅地创建Runnable线程目标类。

(2)使用Lambda表达式优雅地创建Runnable线程目标类。

1.通过匿名类优雅地创建Runnable线程目标类
     package com.crazymakercircle.multithread.basic.create;
     // 省略import
     public class CreateDemo2 {
         public static final int MAX_TURN = 5;
         static int threadNo = 1;
     
         public static void main(String args[]) throws InterruptedException {
             Thread thread = null;
             //使用Runnable的匿名类创建和启动线程
             for (int i = 0; i < 2; i++) {
                 thread = new Thread(new Runnable() { //① 匿名实例
                     @Override
                     public void run() { //② 异步执行的业务逻辑
                         for (int j = 1; j < MAX_TURN; j++) {
                             Print.cfo(getCurThreadName() + ", 轮次:" + j);
                         }
                         Print.cfo(getCurThreadName() + " 运行结束.");
                     }
                 }, "RunnableThread" + threadNo++);
                 thread.start();
             }
     
             Print.cfo(getCurThreadName() + " 运行结束.");
         }
     }
2.使用Lambda表达式优雅地创建Runnable线程目标类
     @FunctionalInterface
     public interface Runnable {
         void run();
     }

@FunctionalInterface注解。该注解的作用是:标记Runnable接口是一个“函数式接口”。

如果一个接口中包含两个或两个以上的抽象方法,就不能使用@FunctionalInterface注解,否则编译会报错。

     package com.crazymakercircle.multithread.basic.create;
     // 省略import
     public class CreateDemo2 {
         public static final int MAX_TURN = 5;
        static int threadNo = 1;
     
         public static void main(String args[]) throws InterruptedException {
             Thread thread = null;
             //使用Lambda表达式形式创建和启动线程
             for (int i = 0; i < 2; i++) {
                 thread = new Thread( ()-> {  //①Lambda表达式
                         for (int j = 1; j < MAX_TURN; j++) {
                            Print.cfo(getCurThreadName() + ", 轮次:" + j); 
                         }
                         Print.cfo(getCurThreadName() + " 运行结束.");
                    }, "RunnableThread" + threadNo++); 
                 thread.start();
             }
     
             Print.cfo(getCurThreadName() + " 运行结束.");
         }
     }
Runnable接口的方式创建线程目标类的优缺点:

缺点

(1)所创建的类并不是线程类,而是线程的target执行目标类,需要将其实例作为参数传入线程类的构造器,才能创建真正的线程。

(2)如果访问当前线程的属性(甚至控制当前线程),不能直接访问Thread的实例方法,必须通过Thread.currentThread()获取当前线程实例,才能访问和控制当前线程。

优点

(1)可以避免由于Java单继承带来的局限性。

(2)逻辑和数据更好分离。

Thread和Runnable区别:

(1)通过继承Thread类实现多线程能更好地做到多个线程并发地完成各自的任务,访问各自的数据资源。

(2)通过实现Runnable接口实现多线程能更好地做到多个线程并发地完成同一个任务,访问同一份数据资源。多个线程的代码逻辑可以方便地访问和处理同一个共享数据资源(如例子中的MallGoods.goodsAmount),这样可以将线程逻辑和业务数据进行有效的分离,更好地体现了面向对象的设计思想。

(3)通过实现Runnable接口实现多线程时,如果数据资源存在多线程共享的情况,那么数据共享资源需要使用原子类型(而不是普通数据类型),或者需要进行线程的同步控制,以保证对共享数据操作时不会出现线程安全问题。

使用Callable和FutureTask创建线程

继承Thread类或者实现Runnable接口这两种方式来创建线程类,但是这两种方式有一个共同的缺陷:不能获取异步执行的结果。

1.Callable接口

Callable接口位于java.util.concurrent包中,查看它的Java源代码,如下:

     package java.util.concurrent;
     @FunctionalInterface
     public interface Callable<V> {
         V call() throws Exception;
     }

Callable接口是一个泛型接口,也是一个“函数式接口”。其唯一的抽象方法call()有返回值,返回值的类型为Callable接口的泛型形参类型。call()抽象方法还有一个Exception的异常声明,容许方法的实现版本的内部异常直接抛出,并且可以不予捕获。

Runnable的唯一抽象方法run()没有返回值,也没有受检异常的异常声明

Callable接口实例没有办法作为Thread线程实例的target来使用。

Callable接口与Thread线程之间起到搭桥作用的重要接口RunnableFuture

这个重要的中间搭桥接口就是RunnableFuture接口,该接口与Runnable接口、Thread类紧密相关

RunnnableFuture接口
     package java.util.concurrent;
     
     public interface RunnableFuture<V>  extends  Runnable, Future<V> {
         void run();
     }

RunnableFuture继承了Runnable接口,从而保证了其实例可以作为Thread线程实例的target目标;同时,RunnableFuture通过继承Future接口,保证了可以获取未来的异步执行结果。

Future接口Future接口至少提供了三大功能:

(1)能够取消异步执行中的任务。

(2)判断异步任务是否执行完成。

(3)获取异步任务完成后的执行结果。

Future接口的源代码如下:
     package java.util.concurrent;
     public interface Future<V> {
         boolean cancel(boolean mayInterruptRunning); //取消异步执行
         boolean isCancelled();
         boolean isDone();//判断异步任务是否执行完成
         //获取异步任务完成后的执行结果
         V get() throws InterruptedException, ExecutionException;
         //设置时限,获取异步任务完成后的执行结果
         V get(long timeout, TimeUnit unit) throws InterruptedException, 
                                               ExecutionException, TimeoutException;
        ...
     }

V get():获取异步任务执行的结果。注意,这个方法的调用是阻塞性的。如果异步任务没有执行完成,异步结果获取线程(调用线程)会一直被阻塞,一直阻塞到异步任务执行完成,其异步结果返回给调用线程。

V get(Long timeout,TimeUnit unit):设置时限,(调用线程)阻塞性地获取异步任务执行的结果。该方法的调用也是阻塞性的,但是结果获取线程(调用线程)会有一个阻塞时长限制,不会无限制地阻塞和等待,如果其阻塞时间超过设定的timeout时间,该方法将抛出异常,调用线程可捕获此异常。

·boolean isDone():获取异步任务的执行状态。如果任务执行结束,就返回true。·boolean isCancelled():获取异步任务的取消状态。如果任务完成前被取消,就返回true。

·boolean cancel(boolean mayInterruptRunning):取消异步任务的执行。

FutureTask类

FutureTask类是Future接口的实现类,提供了对异步任务的操作的具体实现。但是,FutureTask类不仅实现了Future接口,还实现了Runnable接口,或者更加准确地说,FutureTask类实现了RunnableFuture接口。

RunnableFuture接口很关键,既可以作为Thread线程实例的target目标,又可以获取并发任务执行的结果,是Thread与Callable之间一个非常重要的搭桥角色。但是,RunnableFuture只是一个接口,无法直接创建对象,如果需要创建对象,就需用到它的实现类——FutureTask。

所以说,FutureTask类才是真正的在Thread与Callable之间搭桥的类。

FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask既能作为一个Runnable类型的target执行目标直接被Thread执行,又能作为Future异步任务来获取Callable的计算结果。

FutureTask内部有一个Callable类型的成员——callable实例属性,具体如下:FutureTask内部有一个Callable类型的成员——callable实例属性,具体如下:

     private Callable<V> callable;

callable实例属性用来保存并发执行的Callable<V>类型的任务,并且callable实例属性需要在FutureTask实例构造时进行初始化。

FutureTask类实现了Runnable接口,在其run()方法的实现版本中会执行callable成员的call()方法。

FutureTask内部还有另一个非常重要的Object类型的成员——outcome实例属性:

     private Object outcome;

FutureTask的outcome实例属性用于保存callable成员call()方法的异步执行结果。在FutureTask类的run()方法完成callable成员的call()方法的执行之后,其结果将被保存在outcome实例属性中,供FutureTask类的get()方法获取

使用Callable和FutureTask创建线程的具体步骤

步骤如下:

(1)创建一个Callable接口的实现类,并实现其call()方法,编写好异步执行的具体逻辑,可以有返回值。

(2)使用Callable实现类的实例构造一个FutureTask实例。

(3)使用FutureTask实例作为Thread构造器的target入参,构造新的Thread线程实例。

(4)调用Thread实例的start()方法启动新线程,启动新线程的run()方法并发执行。其内部的执行过程为:启动Thread实例的run()方法并发执行后,会执行FutureTask实例的run()方法,最终会并发执行Callable实现类的call()方法。

(5)调用FutureTask对象的get()方法阻塞性地获得并发线程的执行结果。

     package com.crazymakercircle.multithread.basic.create;
     // 省略import
     public class CreateDemo3 {
         public static final int MAX_TURN = 5;
         public static final int COMPUTE_TIMES = 100000000;
     
     //①创建一个 Callable 接口的实现类
         static class ReturnableTask implements Callable<Long> {
              //②编写好异步执行的具体逻辑,可以有返回值
              public Long call() throws Exception{
                 long startTime = System.currentTimeMillis();
                 Print.cfo(getCurThreadName() + " 线程运行开始.");
                 Thread.sleep(1000);
     
                 for (int i = 0; i < COMPUTE_TIMES; i++) {
                     int j = i * 10000;
                 }
                 long used = System.currentTimeMillis() - startTime;
                 Print.cfo(getCurThreadName() + " 线程运行结束.");
                 return used;
             }
         }
     
         public static void main(String args[]) throws InterruptedException {
             ReturnableTask task=new ReturnableTask();//③
             FutureTask<Long> futureTask = new FutureTask<Long>(task);//④
             Thread thread = new Thread(futureTask, "returnableThread");//⑤
             thread.start();//⑥
             Thread.sleep(500);
             Print.cfo(getCurThreadName() + " 让子弹飞一会儿.");
             Print.cfo(getCurThreadName() + " 做一点自己的事情.");
             for (int i = 0; i < COMPUTE_TIMES / 2; i++) {
                 int j = i * 10000;
             }
     
             Print.cfo(getCurThreadName() + " 获取并发任务的执行结果.");
             try {
                 Print.cfo(thread.getName()+"线程占用时间:"
                                          + futureTask.get());//⑦
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } catch (ExecutionException e) {
                 e.printStackTrace();
             }
             Print.cfo(getCurThreadName() + " 运行结束.");
         }
     }

     [CreateDemo3$ReturnableTask:call]:returnableThread 线程运行开始.
     [CreateDemo3:main]:main 让子弹飞一会儿.
     [CreateDemo3:main]:main 做一点自己的事情.
     [CreateDemo3:main]:main 获取并发任务的执行结果.
     [CreateDemo3$ReturnableTask:call]:returnableThread 线程运行结束.
     [CreateDemo3:main]:returnableThread线程占用时间:1008
     [CreateDemo3:main]:main 运行结束.

一个是执行main()方法的主线程,叫作main;另一个是main线程通过thread.start()方法启动的业务线程,叫作returnableThread。该线程是一个包含FutureTask任务作为target的Thread线程。main线程通过thread.start()启动returnableThread线程之后,会继续自己的事情,returnableThread线程开始并发执行。returnableThread线程首先执行的是thread.run()方法,然后在其中会执行到其target(futureTask任务)的run()方法;接着在这个futureTask.run()方法中会执行futureTask的callable成员的call()方法,这里的callable成员(ReturnableTask实例)是通过FutureTask构造器在初始化时传递进来的、自定义的Callable实现类的实例。

FutureTask的Callable成员的call()方法执行完成后,会将结果保存在FutureTask内部的outcome实例属性中

执行时长返回之后,将被作为结果保存在FutureTask内部的outcome实例属性中。至此,异步的returnableThread线程执行完毕。在main线程处理完自己的事情后(以上实例中是一个消磨时间的循环),通过futureTask的get实例方法获取异步执行的结果。这里有两种情况:(1)futureTask的结果outcome不为空,callable.call()执行完成。在这种情况下,futureTast.get会直接取回outcome结果,返回给main线程(结果获取线程)。(2)futureTask的结果outcome为空,callable.call()还没有执行完。在这种情况下,main线程作为结果获取线程会被阻塞住,一直阻塞到callable.call()执行完成。当执行完后,最终结果会保存到outcome中,futureTask会唤醒main线程,去提取callable.call()执行结果。

通过线程池创建线程

所创建的Thread实例在执行完成之后都销毁了,这些线程实例都是不可复用的。实际上创建一个线程实例在时间成本、资源耗费上都很高,在高并发的场景中,断然不能频繁进行线程实例的创建与销毁,而是需要对已经创建好的线程实例进行复用,这就涉及线程池的技术。Java中提供了一个静态工厂来创建不同的线程池,该静态工厂为Executors工厂类。

1.线程池的创建与执行目标提交
通过Executors工厂类创建一个线程池
     //创建一个包含三个线程的线程池
     private static ExecutorService pool = Executors.newFixedThreadPool(3);

示例通过工厂类Executors的newFixedThreadPool(intthreads)方法创建了一个线程池,所创建的线程池的类型为ExecutorService。工厂类的newFixedThreadPool(int threads)方法用于创建包含固定数目的线程池,示例中的线程数量为3。

ExecutorService是Java提供的一个线程池接口,每次我们在异步执行target目标任务的时候,可以通过ExecutorService线程池实例去提交或者执行。ExecutorService实例负责对池中的线程进行管理和调度,并且可以有效控制最大并发线程数,提高系统资源的使用率,同时提供定时执行、定频执行、单线程、并发数控制等功能。向ExecutorService线程池提交异步执行target目标任务的常用方法有:

     //方法一:执行一个 Runnable类型的target执行目标实例,无返回
     void execute(Runnable command);
     
     //方法二:提交一个 Callable类型的target执行目标实例, 返回一个Future异步任务实例
     <T> Future<T> submit(Callable<T> task);  
                         
     //方法三:提交一个 Runnable类型的target执行目标实例, 返回一个Future异步任务实例
     Future<?> submit(Runnable task);

2.线程池的使用实战使用Executors创建线程池,然后使用ExecutorService线程池执行或者提交target执行目标实例的示例代码,大致如下:

     package com.crazymakercircle.multithread.basic.create;
     // 省略import
     public class CreateDemo4
     {
     
         public static final int MAX_TURN = 5;
         public static final int COMPUTE_TIMES = 100000000;
     
         //创建一个包含三个线程的线程池
         private static ExecutorService pool = Executors.newFixedThreadPool(3);
     
         static class DemoThread implements Runnable
         {
             @Override
        public void run()
             {
                 for (int j = 1; j < MAX_TURN; j++)
                 {
                     Print.cfo(getCurThreadName() + ", 轮次:" + j);
                     sleepMilliSeconds(10);
                 }
             }
         }
     
     
         static class ReturnableTask implements Callable<Long>
         {
             //返回并发执行的时间
             public Long call() throws Exception
             {
                 long startTime = System.currentTimeMillis();
                 Print.cfo(getCurThreadName() + " 线程运行开始.");
                 for (int j = 1; j < MAX_TURN; j++)
                 {
                     Print.cfo(getCurThreadName() + ", 轮次:" + j);
                     sleepMilliSeconds(10);
                 }
                 long used = System.currentTimeMillis() - startTime;
                 Print.cfo(getCurThreadName() + " 线程运行结束.");
                 return used;
             }
         }
     
         public static void main(String[] args) {
     
             pool.execute(new DemoThread()); //执行线程实例,无返回
             pool.execute(new Runnable()
             {
                 @Override
                 public void run()
                 {
                     for (int j = 1; j < MAX_TURN; j++)
                     {
                         Print.cfo(getCurThreadName() + ", 轮次:" + j);
                         sleepMilliSeconds(10);
                     }
                 }
             });
             //提交Callable 执行目标实例,有返回
             Future future = pool.submit(new ReturnableTask());
             Long result = (Long) future.get();
             Print.cfo("异步任务的执行结果为:" + result);
             sleepSeconds(Integer.MAX_VALUE);
         }
     }
     [CreateDemo4$DemoThread.run]:pool-1-thread-1, 轮次:1
     [CreateDemo4$1.run]:pool-1-thread-2, 轮次:1
     [CreateDemo4$1.run]:pool-1-thread-2, 轮次:2
     [CreateDemo4$DemoThread.run]:pool-1-thread-1, 轮次:2
     [CreateDemo4$DemoThread.run]:pool-1-thread-1, 轮次:3
     [CreateDemo4$1.run]:pool-1-thread-2, 轮次:3
     [CreateDemo4$DemoThread.run]:pool-1-thread-1, 轮次:4
     [CreateDemo4$1.run]:pool-1-thread-2, 轮次:4
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3 线程运行开始.
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3, 轮次:1
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3, 轮次:2
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3, 轮次:3
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3, 轮次:4
     [CreateDemo4$ReturnableTask.call]:pool-1-thread-3 线程运行结束.
     [CreateDemo4.main]:异步任务的执行结果为:45

ExecutorService线程池的execute(...)与submit(...)方法的区别如下。(1)接收的参数不一样submit()可以接收两种入参:无返回值的Runnable类型的target执行目标实例和有返回值的Callable类型的target执行目标实例。而execute()仅仅接收无返回值的target执行目标实例,或者无返回值的Thread实例。(2)submit()有返回值,而execute()没有submit()方法在提交异步target执行目标之后会返回Future异步任务实例,以便对target的异步执行过程进行控制,比如取消执行、获取结果等。execute()没有任何返回,target执行目标实例在执行之后没有办法对其异步执行过程进行控制,只能任其执行,直到其执行结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值