Java 中要想实现多线程代码有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口
开发工程中,建议使用 Runnable 接口实现多线程
一、继承 Thread 类
Thread 类是在 java.lang
包中定义的,一个类只要继承了 Thread 类,此类就称为多线程操作类。在 Thread 子类中,必须明确复写 Thread 类中的 run()
方法,此方法为线程的主体。
class 类名称 extends Thread{//继承 Thread 类
属性;//类中定义属性
方法;//类中定义方法
public void run(){//覆写 Thread 类中的 run() 方法,此方法时线程的主体
线程主体;
}
}
程序的调用从 Thread 类中继承而来的 start()
方法,而不是 run()方法。
class MyThread extends Thread{//继承 Thread 类
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){//覆写 Thread 类中的 run() 方法
for(int i=0;i<10;i++){//循环 10 次输出
System.out.println(name + "运行,i = " + i);
}
}
}
public class Root{
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程 A");//实例化对象
MyThread mt2 = new MyThread("线程 B");//实例化对象
mt1.start();//启动多线程
mt2.start();//启动多线程
}
}
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
由结果(可能结果的一种)可知,两个线程现在是交错运行,哪个线程对象抢到了 CPU资源,哪个线程就可以运行,所以程序每次的运行结果肯定是不一样的,在线程启动时虽然是调用的是 start()
方法,但是实际上调用的却是 run()
方法的主
如果一个类通过继承 Thread 类来实现,那么只能调用一次 start()
方法,如果调用多次,则将会抛出“IllegalThreadStateException” 异常
多线程的实现需要依靠底层操作系统支持
二、实现 Runnable 接口
在 Java 中也可以通过实现 Runnable 接口的方式实现多线程,Runnable 接口中只定义了一个抽象方法:
public void run();
- 1
使用 Runnable 接口实现多线程的格式如下:
class 类名称 implements Runnable{//实现 Runnable 接口
属性...;
方法...;
public void run(){//覆写 Runnable 接口中的 run() 方法
线程主体;
}
}
示例:
class MyThread implements Runnable{//继承 Runnable 接口
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){//覆写 Thread 类中的 run() 方法
for(int i=0;i<5;i++){//循环 10 次输出
System.out.println(name + "运行,i = " + i);
}
}
}
- 8
- 9
- 10
- 11
这里通过 Runnable 接口实现了多线程,如果想要实现 Runnable 接口,还是要依靠 Thread 类完成启动,在 Thread 类中提供了
public Thread(Runnable target)
和 public Thread(Runnable target,String name)
两个构造方法。
这两个构造方法都可以接收 Runnable 的子类实例对象,所以就可以依靠此点启动多线程:
class MyThread implements Runnable{//继承 Runnable 接口
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){//覆写 run() 方法
for(int i=0;i<5;i++){//循环 10 次输出
System.out.println(name + "运行,i = " + i);
}
}
}
public class Root{
public static void main(String[] args) {
MyThread my1 = new MyThread("线程 A");//实例化Runnable子类对象
MyThread my2 = new MyThread("线程 B");//实例化Runnable子类对象
Thread t1 = new Thread(my1);//实例化 Thread 类对象
Thread t2 = new Thread(my2);//实例化 Thread 类对象
t1.start();//启动多线程
t2.start();//启动多线程
}
}
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
三、Thread 类和 Runnable 接口
如果一个类继承 Thread 类,则不适合于多个线程共享资源,而实现了 Runnable 接口,就可以方便地实现资源的共享
- 继承 Thread 类不能资源共享
class MyThread extends Thread{//继承 Thread 类
private int ticket = 5;//一共 5 张票
public void run(){//覆写 Thread 类中的 run() 方法
for(int i=0;i<10;i++){//超出票数的循环
if(ticket>0){//判断是否有剩余票
System.out.println("卖票:ticket = " + ticket--);
}
}
}
}
public class Root{
public static void main(String[] args) {
MyThread mt1 = new MyThread();//定义线程对象
MyThread mt2 = new MyThread();//定义线程对象
MyThread mt3 = new MyThread();//定义线程对象
mt1.start();//启动第 1 个线程
mt2.start();//启动第 2 个线程
mt3.start();//启动第 3 个线程
}
}
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
程序通过 Thread 类实现多线程,程序中启动了 3 个线程,但是 3 个线程却分别卖了各自的 5 张票,并没有达到资源共享的目的。
这里也不能像 Runnable 一样实例化一个,启动两个start,因为如果一个类通过继承 Thread 类来实现,那么只能调用一次 start() 方法,否则就会报错。
错误的代码
public class Root{
public static void main(String[] args) {
MyThread mt1 = new MyThread();//定义线程对象
mt1.start();//启动第 1 个线程
mt1.start();//启动第 1 个线程
}
}
- 实现 Runnable 接口可以资源共享
class MyThread implements Runnable{//继承 Runnable 接口
private int ticket = 5;//一共 5 张票
public void run(){//覆写 Thread 类中的 run() 方法
for(int i=0;i<10;i++){//超出票数的循环
if(ticket>0){//判断是否有剩余票
System.out.println("卖票:ticket = " + ticket--);
}
}
}
}
public class Root{
public static void main(String[] args) {
MyThread my = new MyThread();//定义线程对象
//启动两个线程来完成这个线程对象
new Thread(my).start();//启动第 1 个线程
new Thread(my).start();//启动第 2 个线程
}
}
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里的意思是实例化一个线程对象,通过两个线程同时去执行它,这个过程中还是通过 Thread 来启动多个线程的。
Runnable 接口相对于继承 Thread 类的优势:
- 适合多个相同程序代码的线程去处理同一资源的情况
- 可以避免由于 Java 的单继承特性带来的局限
- 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的