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

作者:android_小路 时间:2022-10-20 21:34:08 

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张票

}

运行结果:

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

可以看到 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张票
 }

运行结果:

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

可以看到 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();
   }

}

运行结果:

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

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

4.三种方式的对比

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

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

优势: 编写简单

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

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

优势:

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

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

来源:http://blog.csdn.net/android_freshman/article/details/53787011

标签:Java,线程,区别
0
投稿

猜你喜欢

  • Mybatis-Plus 多表联查分页的实现代码

    2023-11-25 04:59:07
  • Spring中AOP概念与两种动态代理模式原理详解

    2023-04-13 01:21:54
  • Android编程之TextView的字符过滤功能分析

    2023-05-19 20:23:36
  • Android实战教程第六篇之一键锁屏应用问题解决

    2023-01-28 03:13:22
  • android开机自动启动app的解决方法

    2021-08-23 18:56:51
  • C# 创建、部署和调用WebService简单示例

    2023-08-15 13:50:07
  • Java并发系列之AbstractQueuedSynchronizer源码分析(共享模式)

    2022-06-03 13:39:58
  • Android Studio中统一管理版本号引用配置问题

    2023-03-06 04:23:54
  • c++指针使用形参改变实参的方法

    2023-03-11 22:40:21
  • Unity3D获取当前键盘按键及Unity3D鼠标、键盘的基本操作

    2023-06-01 18:01:45
  • Java包装类的概述与应用

    2022-03-14 07:50:43
  • java开发分布式服务框架Dubbo暴露服务过程详解

    2021-07-19 17:59:50
  • Unity3D仿写Button面板事件绑定功能

    2023-03-14 10:38:14
  • C#9.0新特性详解——顶级程序语句(Top-Level Programs)

    2023-07-05 18:46:03
  • WinForm实现最小化到系统托盘方法实例详解

    2023-10-05 05:53:30
  • Java使用组合模式实现表示公司组织结构功能示例

    2022-07-31 08:03:37
  • Android 创建与解析XML(四)——详解Pull方式

    2023-06-03 07:10:23
  • android自定义View实现简单五子棋游戏

    2022-09-16 14:52:30
  • Django之多对多查询与操作方法详解

    2021-08-03 03:21:58
  • Java实现在线聊天功能

    2021-10-18 22:16:23
  • asp之家 软件编程 m.aspxhome.com