在 Java 的 JDK 开发包中,已经自带了对多线程技术的支持,可以方便地进行多线程编程。实现多线程编程的方式主要有两种:一种是继承 Thread 类,另一种是实现 Runnable 接口。下面详细介绍这两种具体实现方式。

继承 Thread 类

在学习如何实现多线程前,先来看看 Thread 类的结构,如下:

public class Thread implements Runnable

从上面的源代码可以发现,Thread 类实现了 Runnable 接口,它们之间具有多态关系。

其实,使用继承 Thread 类的方式实现多线程,最大的局限就是不支持多继承,因为 Java 语言的特点就是单根继承,所以为了支持多继承,完全可以实现 Runnable 接口的方式,一边实现一边继承。但用这两种方式创建的线程在工作时的性质是一样的,没有本质的区别。

Thread 类有如下两个常用构造方法:

public Thread(String threadName)
public Thread()

继承 Thread 类实现线程的语法格式如下:

public class NewThreadName extends Thread
{    //NewThreadName 类继承自 Thread 类public void run(){//线程的执行代码在这里}
}

线程实现的业务代码需要放到 run() 方法中。当一个类继承 Thread 类后,就可以在该类中覆盖 run() 方法,将实现线程功能的代码写入 run() 方法中,然后同时调用 Thread 类的 start() 方法执行线程,也就是调用 run() 方法。

Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在 run() 方法中。当执行一个线程程序时,就会自动产生一个线程,主方法正是在这个线程上运行的。当不再启动其他线程时,该程序就为单线程程序。主方法线程启动由 Java 虚拟机负责,开发人员负责启动自己的线程。

如下代码演示了如何启动一个线程:

new NewThreadName().start();    //NewThreadName 为继承自 Thread 的子类

注意:如果 start() 方法调用一个已经启动的线程,系统将会抛出 IllegalThreadStateException 异常。

例 1
编写一个 Java 程序演示线程的基本使用方法。这里创建的自定义线程类为 MyThread,此类继承自 Thread,并在重写的 run() 中输出一行字符串。

MyThread 类代码如下:

public class MyThread extends Thread
{@Overridepublic void run(){super.run();System.out.println("这是线程类 MyThread");}
}

接下来编写启动 MyThread 线程的主方法,代码如下:

public static void main(String[] args)
{MyThread mythread=new MyThread();    //创建一个线程类mythread.start();    //开启线程System.out.println("运行结束!");    //在主线程中输出一个字符串
}

运行上面的程序将看到如下所示的运行效果。

运行结束!
这是线程类 MyThread

从上面的运行结果来看,MyThread 类中 run() 方法执行的时间要比主线程晚。这也说明在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。同时也验证了线程是一个子任务,CPU 以不确定的方式,或者说以随机的时间来调用线程中的 run() 方法,所以就会出现先打印“运行结束!”,后输出“这是线程类MyThread”这样的结果了。

例 2

上面介绍了线程的调用具有随机性,为了更好地理解这种随机性这里编写了一个案例进行演示。

(1) 首先创建自定义的线程类 MyThread01,代码如下:

public class MyThread extends Thread {public void run(){try {for(int i=0;i<10;i++){int time=(int) (Math.random()*1000);Thread.sleep(time);System.out.println("当前线程名称:"+Thread.currentThread().getName());}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

(2) 接下来编写主线程代码,在这里除了启动上面的 MyThread01 线程外,还实现了 MyThread01 线程相同的功能。主线程的代码如下:

public class Test {public static void main(String[] args){ try{ MyThread thread=new MyThread(); thread.setName("myThread"); thread.start(); for (int i=0;i<10;i++){ int time=(int)(Math.random()*1000); Thread.sleep(time); System.out.println("主线程名称="+Thread.currentThread().getName()); } }catch(InterruptedException e){ e.printStackTrace(); }}}

在上述代码中,为了展现出线程具有随机特性,所以使用随机数的形式来使线程得到挂起的效果,从而表现出 CPU 执行哪个线程具有不确定性。

MyThread类中的 start() 方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的 run() 方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程得到运行,启动线程,具有异步执行的效果。

如果调用代码 thread.run() 就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由 main 主线程来调用 run() 方法,也就是必须等 run() 方法中的代码执行完后才可以执行后面的代码。

这种采用随机数延时调用线程的方法又称为异步调用,程序运行的效果如下所示。

当前线程名称:myThread
主线程名称=main
主线程名称=main
主线程名称=main
当前线程名称:myThread
主线程名称=main
主线程名称=main
当前线程名称:myThread
当前线程名称:myThread
主线程名称=main
当前线程名称:myThread
主线程名称=main
当前线程名称:myThread
主线程名称=main
当前线程名称:myThread
主线程名称=main
当前线程名称:myThread
主线程名称=main
当前线程名称:myThread
当前线程名称:myThread

例 3
除了异步调用之外,同步执行线程 start() 方法的顺序不代表线程启动的顺序。下面创建一个案例演示同步线程的调用。

(1) 首先创建自定义的线程类 MyThread,代码如下:

public class MyThread extends Thread {private int i;public MyThread(int i){super();this.i=i;}public void run(){System.out.println("当前数字:"+i);}
}

(2) 接下来编写主线程代码,在这里创建 10 个线程类 MyThread,并按顺序依次调用它们的 start() 方法。主线程的代码如下:

public class Test {public static void main(String[] args){ MyThread t11=new MyThread(1); MyThread t12=new MyThread(2); MyThread t13=new MyThread(3); MyThread t14=new MyThread(4); MyThread t15=new MyThread(5); MyThread t16=new MyThread(6); MyThread t17=new MyThread(7); MyThread t18=new MyThread(8); MyThread t19=new MyThread(9); MyThread t110=new MyThread(10); t11.start(); t12.start(); t13.start(); t14.start(); t15.start(); t16.start(); t17.start(); t18.start(); t19.start(); t110.start(); }}

程序运行后的结果如下所示,从运行结果中可以看到,虽然调用时数字是有序的,但是由于线程执行的随机性,导致输出的数字是无序的,而且每次顺序都不一样。

当前数字:1
当前数字:5
当前数字:4
当前数字:2
当前数字:3
当前数字:7
当前数字:6
当前数字:8
当前数字:9
当前数字:10

实现 Runnable 接口

如果要创建的线程类已经有一个父类,这时就不能再继承 Thread 类,因为 Java 不支持多继承,所以需要实现 Runnable 接口来应对这样的情况。

实现 Runnable 接口的语法格式如下:

public class thread extends Object implements Runnable

提示:从 JDK 的 API 中可以发现,实质上 Thread 类实现了 Runnable 接口,其中的 run() 方法正是对 Runnable 接口中 run() 方法的具体实现。

实现 Runnable 接口的程序会创建一个 Thread 对象,并将 Runnable 对象与 Thread 对象相关联。Thread 类有如下两个与 Runnable 有关的构造方法:

public Thread(Runnable r);
public Thread(Runnable r,String name);

使用上述两种构造方法之一均可以将 Runnable 对象与 Thread 实例相关联。使用 Runnable 接口启动线程的基本步骤如下。

  1. 创建一个 Runnable 对象。
  2. 使用参数带 Runnable 对象的构造方法创建 Thread 实例。
  3. 调用 start() 方法启动线程。

通过实现 Runnable 接口创建线程时开发人员首先需要编写一个实现 Runnable 接口的类,然后实例化该类的对象,这样就创建了 Runnable 对象。接下来使用相应的构造方法创建 Thread 实例。最后使用该实例调用 Thread 类的 start() 方法启动线程,如图 1 所示。


例 1
编写一个简单的案例演示如何实现 Runnable 接口,以及如何启动线程。

(1) 首先创建一个自定义的 MyRmmable 类,让该类实现 Runnable 接口,并在 run() 方法中输出一个字符串。代码如下:

public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("MyRunnable运行中!");}}

(2) 接下来在主线程中编写代码,创建一个 MyRunnable 类实例,并将该实例作为参数传递给 Thread 类的构造方法,最后调用 Thread 类的 start() 方法启动线程。具体实现代码如下:

public class Test {public static void main(String[] args){ Runnable runnable=new MyRunnable();Thread thread=new Thread(runnable);thread.start();System.out.println("主线程运行结束");}}

如上述代码所示,启动线程的方法非常简单。运行结果如下所示,同样验证了线程执行的随机性。

主线程运行结束!
MyRunnable运行中!

注意:要启动一个新的线程,不是直接调用 Thread 子类对象的 run() 方法,而是调用 Thread 子类的 start() 方法。Thread 类的 start() 方法会产生一个新的线程,该线程用于执行 Thread 子类的 run() 方法。

两种方法的比较

虽然 Thread 类和 Runnable 接口都可以创建线程,但是它们也都有各自的优缺点。

  1. 继承 Thread 类的优缺点

当一个 run() 方法体现在继承 Thread 类中时,可以用 this 指向实际控制运行的 Thread 实例。因此,代码不需要使用以下控制语句:

Thread.currenThread().sleep();

不再使用上面的控制语句,而是可以简单地使用 Threadsleep() 方法,继承 Thread 类的方式使代码变得简单易读。

  1. 实现 Runnable 接口的优缺点

从面向对象的角度来看,Thread 类是一个虚拟处理机严格的封装,因此只有当处理机模型修改或扩展时,才应该继承该类。由于 Java 技术只允许单一继承,因此如果已经继承了 Thread 类,就不能再继承其他任何类,这会使用户只能采用实现 Runnable 接口的方式创建线程。

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一

Java多线程的实现方式-Thread 类,Runnable 接口相关推荐

  1. Java多线程-线程的创建(Thread类的基本使用)

    文章目录 一. 线程和Thread类 1. 线程和Thread类 1.1 Thread类的构造方法 1.2 启用线程的相关方法 2. 创建第一个Java多线程程序 3. 使用Runnable对象创建线 ...

  2. Java多线程(2)--Thread类继承和Runnable接口创建线程

    Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现. Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run() ...

  3. java线程如何继承,java多线程(一)之继承Thread类

    一.概述 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径 二.两种实现方式, 下面为第一种方式: 继承Thread类 ...

  4. Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)

    1.守护线程 代码如下: import java.util.concurrent.locks.*; public class StopTest implements Runnable {private ...

  5. JAVA单线程以及java多线程的实现方式

    1.java单线程的实现 public class SingletonThread {@SuppressWarnings("static-access")public static ...

  6. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  7. java编程之线程,继承Thread类,匿名内部类的写法

    package ThreadTest; //java编程之线程,继承Thread类,匿名内部类的写法 public class Test3 {public static void main(Strin ...

  8. Java多线程:实现方式Thread与Runnable

    转载自  Java多线程:实现方式 在Java中, 多线程的实现有两种方式: 扩展java.lang.Thread类 实现java.lang.Runnable接口 方法1 /** * @Descrip ...

  9. java多线程的实现方式_Java 多线程(一)——多线程的实现方式

    一.前言 Java 异常的处理方式与自定义异常 我们已经讲完了,从今天开始我们来学习多线程. 二.与多线程相关的概念 2.1.并发与并行并发:指两个或多个事件在同一个时间段内发生,具体如下图所示: 并 ...

最新文章

  1. cron每2天跑一次_直购直测,进口新极光每2年或34000公里才需要保养一次?
  2. android 瀑布流_软件工程过程模型之瀑布模型
  3. C Primer Plus 第9章 函数 9.4 多源代码文件程序的编译
  4. C++动态空间申请、动态对象(new与delete运算)
  5. 【Linux】15 张 Vim 速查表奉上,帮你提高 N 倍效率!
  6. 如何使用DDMS读取data/data目录下的文件
  7. 分享40个超棒的CSS3按钮教程
  8. lightoj 1382 - The Queue(树形dp)
  9. 数学之美:余弦定理和新闻分类
  10. sk hynix 固态硬盘 管理_5000MB/s 极速狂飙——希捷酷玩FireCuda 520 PCIE4.0 SSD固态硬盘体验...
  11. 爱奇艺真的有1亿付费会员?十五扒了扒用户数据告诉你更多真相
  12. 幸运数字c语言编程软件,幸运数 (C++代码)
  13. 一个创业小公司老板的管理经验
  14. 判断一个多边形是否是凸多边形
  15. npm和package是什么
  16. 算法复习——动态规划篇之最长公共子序列问题
  17. Webpack搭建本地服务器
  18. 软件魔方制作系统启动盘并安装win10系统
  19. Kindle 2.3发布了!
  20. javascript trim方法

热门文章

  1. 学写网站(一)前端配置之安装nvm、node、npm
  2. Gluon.vision的几类数据集
  3. bootstrap 列表--水平定义列表
  4. Ganglia的配置,用于监测系统和Hadoop性能
  5. .NET通过RFC读取SAP数据
  6. 【Leafletjs】4.L.Map 中文API
  7. mac svn .a文件的上传方法
  8. 用C#语言构造蜘蛛程序
  9. C/C++ 类默认生成的四个函数
  10. ESP8266-SDK编写的TM1668程序驱动数码管