Java多线程的实现方式-Thread 类,Runnable 接口
在 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 接口启动线程的基本步骤如下。
- 创建一个 Runnable 对象。
- 使用参数带 Runnable 对象的构造方法创建 Thread 实例。
- 调用 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 接口都可以创建线程,但是它们也都有各自的优缺点。
- 继承 Thread 类的优缺点
当一个 run() 方法体现在继承 Thread 类中时,可以用 this 指向实际控制运行的 Thread 实例。因此,代码不需要使用以下控制语句:
Thread.currenThread().sleep();
不再使用上面的控制语句,而是可以简单地使用 Threadsleep() 方法,继承 Thread 类的方式使代码变得简单易读。
- 实现 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 接口相关推荐
- Java多线程-线程的创建(Thread类的基本使用)
文章目录 一. 线程和Thread类 1. 线程和Thread类 1.1 Thread类的构造方法 1.2 启用线程的相关方法 2. 创建第一个Java多线程程序 3. 使用Runnable对象创建线 ...
- Java多线程(2)--Thread类继承和Runnable接口创建线程
Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现. Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run() ...
- java线程如何继承,java多线程(一)之继承Thread类
一.概述 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径 二.两种实现方式, 下面为第一种方式: 继承Thread类 ...
- Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)
1.守护线程 代码如下: import java.util.concurrent.locks.*; public class StopTest implements Runnable {private ...
- JAVA单线程以及java多线程的实现方式
1.java单线程的实现 public class SingletonThread {@SuppressWarnings("static-access")public static ...
- Java 多线程的基本方式
Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):
- java编程之线程,继承Thread类,匿名内部类的写法
package ThreadTest; //java编程之线程,继承Thread类,匿名内部类的写法 public class Test3 {public static void main(Strin ...
- Java多线程:实现方式Thread与Runnable
转载自 Java多线程:实现方式 在Java中, 多线程的实现有两种方式: 扩展java.lang.Thread类 实现java.lang.Runnable接口 方法1 /** * @Descrip ...
- java多线程的实现方式_Java 多线程(一)——多线程的实现方式
一.前言 Java 异常的处理方式与自定义异常 我们已经讲完了,从今天开始我们来学习多线程. 二.与多线程相关的概念 2.1.并发与并行并发:指两个或多个事件在同一个时间段内发生,具体如下图所示: 并 ...
最新文章
- cron每2天跑一次_直购直测,进口新极光每2年或34000公里才需要保养一次?
- android 瀑布流_软件工程过程模型之瀑布模型
- C Primer Plus 第9章 函数 9.4 多源代码文件程序的编译
- C++动态空间申请、动态对象(new与delete运算)
- 【Linux】15 张 Vim 速查表奉上,帮你提高 N 倍效率!
- 如何使用DDMS读取data/data目录下的文件
- 分享40个超棒的CSS3按钮教程
- lightoj 1382 - The Queue(树形dp)
- 数学之美:余弦定理和新闻分类
- sk hynix 固态硬盘 管理_5000MB/s 极速狂飙——希捷酷玩FireCuda 520 PCIE4.0 SSD固态硬盘体验...
- 爱奇艺真的有1亿付费会员?十五扒了扒用户数据告诉你更多真相
- 幸运数字c语言编程软件,幸运数 (C++代码)
- 一个创业小公司老板的管理经验
- 判断一个多边形是否是凸多边形
- npm和package是什么
- 算法复习——动态规划篇之最长公共子序列问题
- Webpack搭建本地服务器
- 软件魔方制作系统启动盘并安装win10系统
- Kindle 2.3发布了!
- javascript trim方法