Java 创建线程的方式 创建Thread 创建 线程 多线程 三种方式 创建多线程

java线程创建单例实例 java创建一个线程_面试

 

                                                                                         微信公众号

 

java线程创建单例实例 java创建一个线程_并发编程_02

                                                    王皓的GitHub:https://github.com/TenaciousDWang

       创建一个Java线程有三种方法,第一种:继承Thread类,第二种实现Runnable接口,第三种实现callable接口。

 

第一种创建MyThread类继承Thread类,重写run方法,run方法中为该线程所要执行的逻辑。

 

java线程创建单例实例 java创建一个线程_java线程创建单例实例_03

 

创建一个ThreadTest类使用main方法创建MyThread线程,命名为thread0,这里我们也打印一下主线程的ID号,Java采用的是单线程模型,如过不主动创建线程,将只有一个主线程执行任务,需要注意在JVM中并不是只有一个线程,还会存在例如垃圾回收器这样的线程在跑。

 

java线程创建单例实例 java创建一个线程_多线程_04

 

创建完线程后,我们使用start方法来启动线程,注意这里是使用start方法而不是调用我们重写的run方法,如果使用run方法,则跟我们正常调用类中方法没有区别。我们来运行一下main方法,得到如下结果。

 

java线程创建单例实例 java创建一个线程_java线程创建单例实例_05

 

第二种方法,实现Runnable接口,相对于第一种方式,推荐第二种,第一种方法使用继承方式不符合里氏代换原则(Liskov Substitution Principle LSP),实现Runnable接口可以使接口更灵活,对外暴露的细节比较少,使我们更能专注于实现线程的run()方法上。

 

java线程创建单例实例 java创建一个线程_多线程_06

        

由于Runnable就是一个函数式接口:他只有一个方法run()方法。

 

java线程创建单例实例 java创建一个线程_多线程_07

 

根据JDK8新特性,我们这里先用lambda表达式写一下。以前的写法如下,大家也可以对比一下。

 

java线程创建单例实例 java创建一个线程_面试_08

java线程创建单例实例 java创建一个线程_多线程_09

 

运行得到的结果是一样的。

 

第三种实现Callable接口,一下所述都来自于java.util.concurrent包(JUC)。我们来看一下call()方法,这个方法与run方法的定义差不多,但是相对于前两种方法,执行call()方法可以获得返回值,弥补了前两种方法执行完成后,无法直接获取执行结果,需要借助共享变量获取的缺陷,使用Callable与Future则可以很好的解决了这个问题。

 

java线程创建单例实例 java创建一个线程_并发编程_10

 

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。其次我们看到call()方法可以直接抛出异常,而Runnable只能通过setDefaultUncaughtExceptionHandler()的方式才能在主线程中捕获子线程中的异常,使用方法可以参考这篇文章中该方法的具体的用法。

 

       callable一般情况下是配合ExecutorService来使用的,这里面提供了三个重载方法,我们只说常用的两个方法。

 

java线程创建单例实例 java创建一个线程_面试_11

java线程创建单例实例 java创建一个线程_面试_12

        

我们看到这两个方法都返回一个Future对象,Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

 

java线程创建单例实例 java创建一个线程_面试_13

 

五种方法,作用分别是(感兴趣也可以看源码的注释)。

 

  1. cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

 

  1. isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  2. isDone方法表示任务是否已经完成,若任务完成,则返回true;
  3. get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  4. get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

 

 

由于Future<V>是个接口,我们无法直接使用,JDK为我们提供了

FutureTask<V>实现RunnableFuture<V>,RunnableFuture<V>继承了Runnable,与Future<V>。

 

java线程创建单例实例 java创建一个线程_多线程_14

java线程创建单例实例 java创建一个线程_并发编程_15

 

       FutureTask是Future接口的一个唯一实现类,提供了2个构造器。

 

java线程创建单例实例 java创建一个线程_java_16

 

接下来我们来看一下如何使用,首先我们先创建一个MyCallable类,并重写call()方法,返回值与接口泛型一致为Integer。

 

java线程创建单例实例 java创建一个线程_面试_17

 

创建一个CallableTest,在main方法中创建线程池提交callable,并获得结果,注意get方法是阻塞式的,我们在控制台打印一些信息来观察。

 

java线程创建单例实例 java创建一个线程_java_18

        

以下是控制台打印结果。

 

java线程创建单例实例 java创建一个线程_java_19

 

最后,如果没有返回值可以可以声明 Future<?> 形式类型,返回null即可。