多线程的实现

xiaoxiao2021-02-27  215

在JAVA中,有两种方式可以实现多线程。

1、继承Thread类;

2、实现Runnable(Callable)接口。

JDK从最开始定义多线程支持时,只有两种实现要求:要么继承Thread类,要么实现Runnable接口,但是在JDK1.5开始又提供了一个新的线程接口:Callable接口。

继承Thread实现多线程:

java.long.Thread类是一个负责线程操作的类,任何类只需要继承Thread类就可以成为一个线程 的主类。但是既然是主类就必须有它的使用方法,而线程启动的主方法需要覆写Thread类中的run()方法实现,线程主体类的定义格式如下:

class 类名称 extends Thread{//继承Thread类 属性... ; //类中定义属性 方法... ; //类中定义方法 public void run(){ //覆写Thread类中的run()方法,此方法是线程的主体 线程主体方法 ; } }

定义一个线程操作类:

class MyThread extends Thread{//这是一个多线程的操作类 private String name ; //定义类中的属性 public MyThread(String name ){//定义构造方法 this.name = name ; } @Override public void run(){//覆写run()方法,作为线程的主操作方法 for(int i = 0 ; i < 200 ; i ++){ System.out.println(this.name + "---->" + x) ; } } }

本程序线程的主要功能是实现循环的输出操作,所有的线程与进程都是一样的,必须轮流去抢占资源,所以多线程的执行应该是多个线程彼此交替执行。也就是说,直接调用run()方法,并不能启动多线程,多线程的唯一启动方法就是Thread类中的start()方法:public void start()(调用此方法执行方法体是run()方法定义的代码)。

启动多线程:

public class Demo{ public static void main (String [] args){ MyThread mt1 = new Mythread("线程1") ;//实例化多线程类对象 MyThread mt2 = new Mythread("线程2") ; MyThread mt3 = new Mythread("线程3") ; mt1.start() ;//启动多线程 mt2.start() ; mt3.start() ; } }

疑问:为什么多线程启动不是调用run()方法而是调用start()??

为了解释多线程的启动调用的问题,可以查看java.long.Thread类的start()方法的源代码:

public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0();

可以发现在start()方法里面要调用一个start0()方法,而且此方法的结构与抽象方法类似,使用了native声明。

在JAVA的开发中有一门技术被称为JNI(Java Native Interface)技术,这门技术的特点是使用Java调用本机操作系统提供的函数。但是此技术有一个缺点,就是离不开特定的操作系统。如果想要执行线程,需要操作系统来进行资源分配,所以此操作严格来主要是由JVM负责根据不同的操作系统而实现的。即使用Thread类的start()方法不仅要启动多线程的执行代码,还要根据不同的操作系统进行资源的分配。

另外,通过上述代码可以发现,在Thread类的start()方法中会抛出一个“IllegalThreadStateException”异常,本方法里面使用了throw抛出异常,本应该用try...catch...捕获异常,或者在start()方法是使用throws声明,但是此处并没有这样做,因为此异常是继承自“RuntimeException”,这样就可以由用户选择性进行性处理,如果某一个线程兑现进行了重复启动(同一个线程对象调用多次start()方法),就会抛出此异常。

实现Runnable接口:

使用Thread类的确可以方便的进行多线程的实现,但是这种方式最大的缺点就是单继承局限性,为此,在Java中也可以利用Runnable接口来实现多线程。而这个接口的定义如下:

@FunctionalInterface interface Runnable{ public void run() ; } 在Runnable接口也定义了run()方法,所以只需要覆写run()方法即可。

使用Runnable接口实现多线程:

class MyThread extends implements Runnable{//这是一个多线程的操作类 private String name ; //定义类中的属性 public MyThread(String name ){//定义构造方法 this.name = name ; } @Override public void run(){//覆写run()方法,作为线程的主操作方法 for(int i = 0 ; i < 200 ; i ++){ System.out.println(this.name + "---->" + x) ; } } } 本程序实现了Runnable接口,也正常的覆写了run()方法,但是却会存在一个问题:要启动多线程,必须调用Thread类中的start()方法,如果继承了Thread类,那么可以直接使用Thread类中的start()方法,但是Runnable接口并没有提供可以被继承的start()方法,这是该如何启动多线程呢?

通过观察Thread类可以发现,Thread类中有一个构造方法:public Thread(Runnable target),此方法可以接收一个Runnable接口对象。

利用Thread类启动多线程:

public class Demo{ public static void main (String [] args){ MyThread mt1 = new Mythread("线程1") ;//实例化多线程类对象 MyThread mt2 = new Mythread("线程2") ; MyThread mt3 = new Mythread("线程3") ; new Thread(mt1).start() ;//启动多线程 new Thread(mt2).start() ; new Thread(mt3).start() ; } } 本程序首先利用Thread类的对象包装了Runnable接口对象实例,然后利用了Thread类中的start()方法启动多线程。

因为在Runnable接口的声明处使用了@FunctionalInterface的注解,证明Runnable接口是一个函数式接口,那么我们就可以使用Lambda表达式的风格编写:

public class Demo{ public static void main (String [] args){ String name = "线程对象" ; new Thread(()->{ for(int x = 0 ; x < 200 ; x ++){ System.out.println(name + " - - >" + x ) ; } }).start() ; } }

本程序利用Lambda表达式直接定义的线程主体实现操作,并且依然依靠Thread类的start()方法进行启动,这样的做法比直接用Runnable接口更加方便。

使用Runnable接口可以有效的避免单继承局限问题,所以在实际开发中,对于多线程的实现首选的就是Runnable接口。

面试题:请解释Thread类与Runnable接口实现多线程的区别?(请解释两种多线程实现方式的区别)

      1、Thread类是Runnable接口的子类,使用Runnable接口实现多线程可以避免单继承的局限性。

      2、Runnable接口实现的多线程可以比Thread类实现的多线程更好的描述数据共享这一概念!

转载请注明原文地址: https://www.6miu.com/read-11233.html

最新回复(0)