Java 线程对比(Thread,Runnable,Callable)实例详解

Java 线程对比Thread,Runnable,Callable

java 使用 Thread 类代表线程,所有现场对象都必须是 Thread 类或者其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流。java 使用线程执行体来代表这段程序流。

1.继承Thread 类创建线程

启动多线程的步骤如下:

(1)定义Thread 类的子类,并重写该类的run() 方法,该run() 方法的方法体就代表类线程需要完成的任务。因此把run() 方法称为线程执行体。
(2)创建 Thread 子类的实例,即创建线程对象。
(3)调用线程的star()方法来启动该线程。

相关代码如下:

/**
   * 继承 thread 的内部类,以买票例子
   */
  public class FirstThread extends Thread{
    private int i;
    private int ticket = 10;

    @Override
    public void run() {
      for (;i<20;i++) {
        //当继承thread 时,直接使用this 可以获取当前的线程,getName() 获取当前线程的名字
//        Log.d(TAG,getName()+" "+i);

        if(this.ticket>0){
          Log.e(TAG, getName() + ", 卖票:ticket=" + ticket--);
        }
      }

    }
  }

  private void starTicketThread(){
    Log.d(TAG,"starTicketThread, "+Thread.currentThread().getName());

    FirstThread thread1 = new FirstThread();
    FirstThread thread2 = new FirstThread();
    FirstThread thread3 = new FirstThread();

    thread1.start();
    thread2.start();
    thread3.start();

    //开启3个线程进行买票,每个线程都卖了10张,总共就30张票

  }

运行结果:

可以看到 3 个线程输入的 票数变量不连续,注意:ticket 是 FirstThread 的实例属性,而不是局部变量,但是因为程序每次创建线程对象都需要创建一个FirstThread 的对象,所有多个线程不共享该实例的属性。

2.实现 Runnable 接口创建线程

注意:public class Thread implements Runnable

(1)定义 Runnable 接口的实现类,并重写该接口的run()方法,该run() 方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable 实例类的实例,此实例作为 Thread 的 target 来创建Thread 对象,该Thread 对象才是真正的对象。

相关代码如下:

/**
   * 实现 runnable 接口,创建线程类
   */
  public class SecondThread implements Runnable{

    private int i;
    private int ticket = 100;

    @Override
    public void run() {
      for (;i<20;i++) {
        //如果线程类实现 runnable 接口
        //获取当前的线程,只能用 Thread.currentThread() 获取当前的线程名
        Log.d(TAG,Thread.currentThread().getName()+" "+i);

        if(this.ticket>0){
          Log.e(TAG, Thread.currentThread().getName() + ", 卖票:ticket=" + ticket--);
        }
      }
    }
  }

  private void starTicketThread2(){
    Log.d(TAG,"starTicketThread2, "+Thread.currentThread().getName());

    SecondThread secondThread = new SecondThread();

    //通过new Thread(target,name)创建新的线程
    new Thread(secondThread,"买票人1").start();
    new Thread(secondThread,"买票人2").start();
    new Thread(secondThread,"买票人3").start();

    //虽然是开启了3个线程,但是一共只买了100张票
  }

运行结果:

可以看到 3 个线程输入的 票数变量是连续的,采用 Runnable 接口的方式创建多个线程可以共享线程类的实例的属性。这是因为在这种方式下,程序所创建的Runnable 对象只是线程的 target ,而多个线程可以共享同一个 target,所以多个线程可以共享同一个线程类(实际上应该是该线程的target 类)的实例属性。

3.使用 Callable 和Future 创建线程

从 java 5 开始,Java 提供了 Callable 接口,该接口是runnable 的增强版,Callable 提供类一个 call() 方法可以作为线程执行体,但是call() 方法的功能更强大。

(1) call() 方法可以有返回值
(2) call() 方法可以声明抛出异常

因此我们完全可以提供一个callable 对象作为Thread的 target ,而该线程的执行体就是该callable 对象的call() 方法。同时 java 5 提供了 Future 接口 来代表Callable 接口里 call() 方法的返回值,并且提供了一个 futureTask 的实现类,该实现类实现类 future 接口,并实现了runnable 接口—可以作为Thread 类的target.

启动步骤如下:

(1)创建callable接口的实现类,并实现call() 方法,该call() 方法将作为线程的执行体,且该call() 方法是有返回值的。
(2)创建 callable实现类的实例,使用 FutureTask 类来包装Callable对象,该FutureTask 对象封装 call() 方法的返回值。
(3)使用FutureTask 对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获取子线程执行结束后的返回值。

相关代码如下:

/**
   * 使用callable 来实现线程类
   */
  public class ThirdThread implements Callable<Integer>{

    private int ticket = 20;

    @Override
    public Integer call(){

      for ( int i = 0;i<10;i++) {
        //获取当前的线程,只能用 Thread.currentThread() 获取当前的线程名
//        Log.d(TAG,Thread.currentThread().getName()+" "+i);

        if(this.ticket>0){
          Log.e(TAG, Thread.currentThread().getName() + ", 卖票:ticket=" + ticket--);
        }
      }

      return ticket;
    }
  }

  private void starCallableThread(){
    ThirdThread thirdThread = new ThirdThread();
    FutureTask<Integer> task = new FutureTask<Integer>(thirdThread);

    new Thread(task,"有返回值的线程").start();

    try {
      Integer integer = task.get();
      Log.d(TAG,"starCallableThread, 子线程的返回值="+integer);

    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

  }

运行结果:

注意:Callable的call() 方法允许声明抛出异常,并且允许带有返回值。
程序最后调用FutureTask 对象的get()方法来返回Call()方法的返回值,导致主线程被阻塞,直到call()方法结束并返回为止。

4.三种方式的对比

采用继承Thread 类的方式创建多线程

劣势: 已经继承Thread类不能再继承其他父类。

优势: 编写简单

采用继承Runnable,Callable 接口的方式创建多线程

劣势: 编程稍微有点复杂,如果需要访问当前线程必须使用Thread.currentThread()

优势:

(1)还可以继承其他类
(2)多个线程可以共享一个target 对象,所以非常适合多个相同的线程来处理同一份资源的情况,从而将cpu,代码和数据分开,形成清晰的模型,较好的体现类面向对象的思想。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 浅析Java中Runnable和Thread的区别

    线程的起动并不是简单的调用了你的RUN方法,而是由一个线程调度器来分别调用你的所有线程的RUN方法, 我们普通的RUN方法如果没有执行完是不会返回的,也就是会一直执行下去,这样RUN方法下面的方法就不可能会执行了,可是线程里的RUN方法却不一样,它只有一定的CPU时间,执行过后就给别的线程了,这样反复的把CPU的时间切来切去,因为切换的速度很快,所以我们就感觉是很多线程在同时运行一样. 你简单的调用run方法是没有这样效果的,所以你必须调用Thread类的start方法来启动你的线程.所以你启动

  • java实现多线程的两种方式继承Thread类和实现Runnable接口的方法

    实现方式和继承方式有什么区别呢? *区别: *继承Thread:线程代码存放在Thread子类run方法中 *实现Runnable:线程代码存放在接口的子类的run方法中 *实现方式的好处:避免了单继承的局限性 *在定义线程时,建议使用实现方式,当然如果一个类没有继承父类,那么也可以通过继承Thread类来实现多线程 *注意:Runnable接口没有抛出异常,那么实现它的类只能是try-catch不能throws *Java对多线程的安全问题提供了专业的解决方式就是同步代码块synchroniz

  • java线程之使用Runnable接口创建线程的方法

    实现Runnable接口的类必须使用Thread类的实例才能创建线程.通过Runnable接口创建线程分为两步: 1. 将实现Runnable接口的类实例化. 2. 建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法. 最后通过Thread类的start方法建立线程. 下面的代码演示了如何使用Runnable接口来创建线程: 复制代码 代码如下: package mythread; public class MyRunnable implements Runn

  • java多线程编程之使用runnable接口创建线程

    1.将实现Runnable接口的类实例化. 2.建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法. 最后通过Thread类的start方法建立线程.下面的代码演示了如何使用Runnable接口来创建线程: package mythread;public class MyRunnable implements Runnable{ public void run() {  System.out.println(Thread.currentThread().get

  • Java中的Runnable,Callable,Future,FutureTask的比较

    Java中的Runnable,Callable,Future,FutureTask的比较 Java中存在Runnable.Callable.Future.FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别. Runnable 其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中, 该函数没有返回值 .然后使用某个线程去执行该runnable即可实现多线程,Thread类在调

  • Java中继承thread类与实现Runnable接口的比较

    Java中线程的创建有两种方式:  1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过实现Runnable接口,实例化Thread类 在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程.当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果. package com.threadtest; class MyThread extends T

  • Java中Runnable和Thread的区别分析

    Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限, 下面看例子: 复制代码 代码如下: package org.thread.demo; class MyThread extends Thread{ private String name; public MyThread(String name) { super(); this.name = name; } publ

  • java Runnable接口创建线程

    java Runnable接口创建线程 创建一个线程,最简单的方法是创建一个实现Runnable接口的类. 为了实现Runnable,一个类只需要执行一个方法调用run(),声明如下: public void run() 你可以重写该方法,重要的是理解的run()可以调用其他方法,使用其他类,并声明变量,就像主线程一样. 在创建一个实现Runnable接口的类之后,你可以在类中实例化一个线程对象. Thread定义了几个构造方法,下面的这个是我们经常使用的: Thread(Runnable th

  • JAVA多线程Thread和Runnable的实现

    java中只允许单一继承,但允许实现多个接口,因此第二种方法更灵活. 复制代码 代码如下: /**     * 运行继承java.lang.Thread类定义的线程     */    public void startOne() {        // 创建实例        OneThread oneThread = new OneThread();        // 启动线程ThreadA        oneThread.startThreadA();        try {    

  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解 前言: 使用JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作.有时候,我们启动N个线程去做一件事情,只有当这N个线程都达到某一个临界点的时候,我们才能继续下面的工作,就是说如果这N个线程中的某一个线程先到达预先定义好的临界点,它必须等待其他N-1线程也到达这个临界点,接下来的工作才能继续,只要这N个线程中有1个线程没有到达所谓的临界点,其他线程就算抢先到达了临界点,也只能等待,只有所有这N

  • JAVA多线程实现生产者消费者的实例详解

    JAVA多线程实现生产者消费者的实例详解 下面的代码实现了生产者消费者的问题 Product.Java package consumerProducer; public class Product { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } public Product(String id) { this.id=id; } publ

  • java 设计模式之单例的实例详解

    java 设计模式之单例的实例详解 设计模式思想 什么是设计模式:我作为初学者,今天第一次正式学习设计模式,我觉得对与理解什么是设计模式很重要,那么什么是设计模式呢? 设计模式:解决问题的一种行之有效的思想. 设计模式:用于解决特定环境下.重复出现的特定问题的解决方案 我的理解是前人在软件设计的时候碰到了一类问题,他们总结出了一套行之有效,并且经过验证的解决方案. 设计模式的优点: 1.设计模式都是一些相对优秀的解决方案,很多问题都是典型的.有代表性的问题,学习设计模式,我们就不用自己从头来解决

  • java实现事件委托模式的实例详解

    java实现事件委托模式的实例详解 举例说明: 一个班级,有两类学生,A类:不学习,玩,但是玩的东西不一样,有的是做游戏,与的是看电视(有点不合理) B类:放哨的学生,专门看老师的动向,如果老师进班了就立即通知大家. 如此就形成了一个需求,放哨的学生要通知所有玩的学生:老师来了,而不同的学生有不同的反应,有的马上把电视关闭,有的停止玩游戏. 设计的要求如下,让A类学生和B类学生完全解耦,即A类完全不知道B类的学生,却可以通知B类的学生. 代码及说明如下: Event 类,定义了一个事件类: pa

  • JAVA 静态的单例的实例详解

    JAVA  静态的单例的实例详解 实现代码: public class Printer { private Printer(){ } public static Printer newInstance(){ return CreatePrinter.mPrinter; } private static class CreatePrinter{ private final static Printer mPrinter = new Printer(); } } 因为静态的单例对象没有作为类的成员变

  • JAVA  静态的单例的实例详解

    JAVA  静态的单例的实例详解 实现代码: public class Printer { private Printer(){ } public static Printer newInstance(){ return CreatePrinter.mPrinter; } private static class CreatePrinter{ private final static Printer mPrinter = new Printer(); } } 因为静态的单例对象没有作为类的成员变

  • Java线程之间的共享与协作详解

    目录 前言 一.进程和线程 1.进程是程序运行资源分配的最小单位 2.线程是CPU 调度的最小单位,必须依赖于进程而存在 3.线程无处不在 二.CPU 核心数和线程数的关系 1.多核心 2.多线程 3.核心数.线程数 三.CPU 时间片轮转机制 四.并行和并发 1.并发 2.并行 五.高并发编程 1.CPU 资源利用的充分 2.加快用户响应时间 3.使代码模块化.异步化.简单化 六.多线程注意事项 1.线程之间的安全性 2.线程之间的死锁 3.线程多了会将服务资源耗尽形成死机.当机 七.多线程注

  • java实现可安装的exe程序实例详解

    java实现可安装的exe程序实例详解 通过编写Java代码,实现可安装的exe文件的一般思路: 1.在eclipse中创建java项目,然后编写Java代码,将编写好的Java项目导出一个.jar格式的jar包: 2.通过安装exe4j软件,将导出的.jar格式的文件制作成.exe格式的可执行的文件,(注意:此时的.exe文件只是可以执行,还不能够安装): 3.通过安装Inno setup软件,将可执行的.exe格式的文件..jar格式的文件以及其它需要的文件制作成一个可安装的.exe格式的文

  • java 中HttpClient传输xml字符串实例详解

    java 中HttpClient传输xml字符串实例详解 介绍:我现在有一个对象page,需要将page对象转换为xml格式并以binary方式传输到服务端 其中涉及到的技术点有: 1.对象转xml流 2.输出流转输入流 3.httpClient发送二进制流数据 POM文件依赖配置 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifact

  • java与JSON数据的转换实例详解

    java与JSON数据的转换实例详解 JSON与JAVA数据的转换(JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互.) 代码中有这么一句,是后台的封装数据. JSONObject jo = JSONObject.fromObject(map); 常见的java代码转换成json --请注意,这个方法曾经给我造成过困惑.因为,它在对Object转换的时候是按照domain类中的所有getXXX()方

随机推荐