方式一:继承Thread类实现多线程:

1. 在Java中负责实现线程功能的类是java.lang.Thread 类。

2. 可以通过创建 Thread的实例来创建新的线程。

3. 每个线程都是通过某个特定的Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。

4. 通过调用Thread类的start()方法来启动一个线程(只是将线程由新生态转为就绪态,而不是运行态)。

代码示例:

  1. public class TestThread extends Thread {//自定义类继承Thread类
  2. //run()方法里是线程体
  3. public void run() {
  4. for (int i = 0; i < 10; i++) {
  5. System.out.println(this.getName() + ":" + i);//getName()方法是返回线程名称
  6. }
  7. }
  8.  
  9. public static void main(String[] args) {
  10. TestThread thread1 = new TestThread();//创建线程对象
  11. thread1.start();//启动线程
  12. TestThread thread2 = new TestThread();
  13. thread2.start();
  14. }
  15. }

此种方式的缺点:因为Java只支持单继承多实现,所以当我们的类已经继承了一个类(如小程序必须继承自 Applet 类),则无法再继承 Thread 类。

方式二:通过Runnable接口实现多线程

  在开发中,我们应用更多的是通过Runnable接口实现多线程。这种方式克服了实现Thread类的缺点,即在实现Runnable接口的同时还可以继承某个类。

代码示例:

  1. public class TestThread2 implements Runnable {//自定义类实现Runnable接口;
  2. //run()方法里是线程体;
  3. public void run() {
  4. for (int i = 0; i < 10; i++) {
  5. System.out.println(Thread.currentThread().getName() + ":" + i);
  6. }
  7. }
  8. public static void main(String[] args) {
  9. //创建线程对象,把实现了Runnable接口的对象作为参数传入;
  10. Thread thread1 = new Thread(new TestThread2());
  11. thread1.start();//启动线程;
  12. Thread thread2 = new Thread(new TestThread2());
  13. thread2.start();
  14. }
  15. }

需要注意的是,start()方法属于thread类的,故自定义类实现Runnable接口后调用start()方法前有如下过程:

  1. Runnable r = new TestThread2();
  2. Thread thread1 = new Thread(r);
  3. //简写为:
  4. Thread thread1 = new Thread(new TestThread2());

方式三:通过Callable接口实现多线程

Callable和Future出现的原因:

    这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
    如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。

  而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

Callable和Future介绍

  Callable接口代表一段可以调用并返回结果的代码;Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。

  Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法在线程池中执行Callable内的任务。由于Callable任务是并行的(并行就是整体看上去是并行的,其实在某个时间点只有一个线程在执行),我们必须等待它返回的结果。
  java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。

 Callable与Runnable

java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

  1. public interface Runnable {
  2. public abstract void run();
  3. }

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

  Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():

  1. public interface Callable<V> {
  2. /**
  3. * Computes a result, or throws an exception if unable to do so.
  4. *
  5. * @return computed result
  6. * @throws Exception if unable to compute a result
  7. */
  8. V call() throws Exception;
  9. }

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

那么怎么使用Callable呢?

一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

  1. <T> Future<T> submit(Callable<T> task);
  2. <T> Future<T> submit(Runnable task, T result);
  3. Future<?> submit(Runnable task);

第一个submit方法里面的参数类型就是Callable。

暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。

一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。

Future

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

  Future类位于java.util.concurrent包下,它是一个接口:

  1. public interface Future<V> {
  2. boolean cancel(boolean mayInterruptIfRunning);
  3. boolean isCancelled();
  4. boolean isDone();
  5. V get() throws InterruptedException, ExecutionException;
  6. V get(long timeout, TimeUnit unit)
  7. throws InterruptedException, ExecutionException, TimeoutException;
  8. }

在Future接口中声明了5个方法,下面依次解释每个方法的作用:

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

isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

isDone方法表示任务是否已经完成,若任务完成,则返回true;

get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:

  1)判断任务是否完成;

  2)能够中断任务;

  3)能够获取任务执行结果。

  因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

FutureTask

FutureTask实现了RunnableFuture接口,这个接口的定义如下:

  1. public interface RunnableFuture<V> extends Runnable, Future<V> {
  2. void run();
  3. }

可以看到这个接口实现了Runnable和Future接口,接口中的具体实现由FutureTask来实现。这个类的两个构造方法如下 :

  1. public FutureTask(Callable<V> callable) {
  2. if (callable == null)
  3. throw new NullPointerException();
  4. sync = new Sync(callable);
  5. }
  6. public FutureTask(Runnable runnable, V result) {
  7. sync = new Sync(Executors.callable(runnable, result));
  8. }

如上提供了两个构造函数,一个以Callable为参数,另外一个以Runnable为参数。这些类之间的关联对于任务建模的办法非常灵活,允许你基于FutureTask的Runnable特性(因为它实现了Runnable接口),把任务写成Callable,然后封装进一个由执行者调度并在必要时可以取消的FutureTask。

  FutureTask可以由执行者调度,这一点很关键。它对外提供的方法基本上就是Future和Runnable接口的组合:get()、cancel、isDone()、isCancelled()和run(),而run()方法通常都是由执行者调用,我们基本上不需要直接调用它。

一个FutureTask的例子

  1. public class MyCallable implements Callable<String> {
  2. private long waitTime;
  3. public MyCallable(int timeInMillis){
  4. this.waitTime=timeInMillis;
  5. }
  6. @Override
  7. public String call() throws Exception {
  8. Thread.sleep(waitTime);
  9. //return the thread name executing this callable task
  10. return Thread.currentThread().getName();
  11. }
  12. }
  1. public class FutureTaskExample {
  2. public static void main(String[] args) {
  3. MyCallable callable1 = new MyCallable(1000); // 要执行的任务
  4. MyCallable callable2 = new MyCallable(2000);
  5.  
  6. FutureTask<String> futureTask1 = new FutureTask<String>(callable1);// 将Callable写的任务封装到一个由执行者调度的FutureTask对象
  7. FutureTask<String> futureTask2 = new FutureTask<String>(callable2);
  8.  
  9. ExecutorService executor = Executors.newFixedThreadPool(2); // 创建线程池并返回ExecutorService实例
  10. executor.execute(futureTask1); // 执行任务
  11. executor.execute(futureTask2);
  12.  
  13. while (true) {
  14. try {
  15. if(futureTask1.isDone() && futureTask2.isDone()){// 两个任务都完成
  16. System.out.println("Done");
  17. executor.shutdown(); // 关闭线程池和服务
  18. return;
  19. }
  20.  
  21. if(!futureTask1.isDone()){ // 任务1没有完成,会等待,直到任务完成
  22. System.out.println("FutureTask1 output="+futureTask1.get());
  23. }
  24.  
  25. System.out.println("Waiting for FutureTask2 to complete");
  26. String s = futureTask2.get(200L, TimeUnit.MILLISECONDS);
  27. if(s !=null){
  28. System.out.println("FutureTask2 output="+s);
  29. }
  30. } catch (InterruptedException | ExecutionException e) {
  31. e.printStackTrace();
  32. }catch(TimeoutException e){
  33. //do nothing
  34. }
  35. }
  36. }
  37. }

运行如上程序后,可以看到一段时间内没有输出,因为get()方法等待任务执行完成然后才输出内容。

输出结果如下:

  1. FutureTask1 output=pool-1-thread-1
  2. Waiting for FutureTask2 to complete
  3. Waiting for FutureTask2 to complete
  4. Waiting for FutureTask2 to complete
  5. Waiting for FutureTask2 to complete
  6. Waiting for FutureTask2 to complete
  7. FutureTask2 output=pool-1-thread-2
  8. Done

原文转载地址:ttps://www.sxt.cn/Java_jQuery_in_action/eleven-runnable-interface.html

       https://www.cnblogs.com/fengsehng/p/6048609.html

创建线程的三种方式(Thread、Runnable、Callable)的更多相关文章

  1. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  2. Java并发编程:Java创建线程的三种方式

    目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...

  3. java创建线程的三种方式及其对比

    第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...

  4. AJPFX总结java创建线程的三种方式及其对比

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...

  5. java创建线程的三种方式及其对照

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...

  6. Java创建线程的三种方式

    一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行体. (2)创建Thread子类的实 ...

  7. 创建线程的第二种方式------实现Runnable接口的方式

    package cn.itcast.demo16.Demo07.Runnable;/** * @author newcityman * @date 2019/7/22 - 23:17 */public ...

  8. 创建线程的三种方式_Callable和Runnable的区别

    Java 提供了三种创建线程的方法 通过实现Runnable接口 通过继承Thread接口 通过Callable和Future创建线程 通过实现 Runnable 接口来创建线程 public cla ...

  9. Java中创建线程的三种方式以及区别

    在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...

随机推荐

  1. SpringBoot配置虚拟化路径用于图片的展示

    前言:springboot默认可以访问resources下的static文件夹下的静态资源,我们一般将图片指定上传到static下的某个文件夹,例如images,开发阶段可以使用,但是当项目打成jar ...

  2. js-关于异步原理的理解和总结

    我们经常说JS是单线程的,比如Node.js研讨会上大家都说JS的特色之一是单线程的,这样使JS更简单明了,可是大家真的理解所谓JS的单线程机制吗?单线程时,基于事件的异步机制又该当如何,这些知识在& ...

  3. 信息论 | information theory | 信息度量 | information measures | R代码(一)

    这个时代已经是多学科相互渗透的时代,纯粹的传统学科在没落,新兴的交叉学科在不断兴起. life science neurosciences statistics computer science in ...

  4. Java里如何将一个字符串重复n次

    程序: import java.util.Collections; public class RepeatString { public static String getRepeatSpace(St ...

  5. StringBuffer & StringBuilder的区别

    StringBuffer是线程安全的,内部有锁.所以比StringBuilder慢一点. 在单线程生成字符串的情况下,优先使用StringBuilder. 这就是为啥有时候IntelliJ Idea会 ...

  6. 海思uboot启动流程详细分析(转)

    海思uboot启动流程详细分析(一) 海思uboot启动流程详细分析(二) 海思uboot启动流程详细分析(三)  

  7. Dart对象和类

    /* 面向对象编程(OOP)的三个基本特征是:封装.继承.多态 封装:封装是对象和类概念的主要特性.封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则 ...

  8. JQuery中formSerialize()报错:对象不支持"formSerialize"属性或方法

    formSerialize()是jQuery的表单插件中提供的方法. formSerialize()的核心方法是:$.param(data); Form表单经过formSerialize(),被序列化 ...

  9. Mockplus更快更简单的原型设计

    更快更简单的原型设计 https://www.mockplus.cn/ Mockplus,更快更简单的原型设计工具.快速创建原型,一键拖拽创建交互,团队协作省事省力.微软.华为.东软.育碧.Oracl ...

  10. 软件定义网络基础---SDN的主流构架

    一:基于不同标准的主流构架 二: ONF定义的SDN基本构架 (一) 四个平面.两大接口 三:四个平面 (一)数据平面 数据平面是由若干网元(Netword Element)构成,每个网元包括一个或多 ...