您的位置:首页 > 编程语言 > Java开发

浅析Java中Runnable和Thread的区别

2018-03-27 16:07 323 查看
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。接下来通过本文给大家浅析Java中Runnable和Thread的区别,一起看看吧

线程的启动并不是简单的调用了你的RUN方法,而是由一个线程调度器来分别调用你的所有线程的RUN方法,

我们普通的RUN方法如果没有执行完是不会返回的,也就是会一直执行下去,这样RUN方法下面的方法就不可能会执行了,可是线程里的RUN方法却不一样,它只有一定的CPU时间,执行过后就给别的线程了,这样反复的把CPU的时间切来切去,因为切换的速度很快,所以我们就感觉是很多线程在同时运行一样.

上面的是什么意思呢? 放在场景中,如果有多个人同时访问一份共享资源,这时候并不是说谁先来谁就去一下子执行完run方法,而是根据CPU给定的时间,这个线程执行一段时间,然后交给另外一个线程。

你简单的调用run方法是没有这样效果的,所以你必须调用Thread类的start方法来启动你的线程.所以你启动线程有两种方法:

一是写一个类继承自Thread类,然后重写里面的run方法,用start方法启动线程。

二是写一个类实现Runnable接口,实现里面的run方法,用new Thread(Runnable target).start()方法来启动。

这两种方法都必须实现RUN方法,这样线程启动的时候,线程管理器好去调用你的RUN方法.

你的TestThread没有继承自Thread类,怎么可能会有start方法呢?

在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。

下面看例子:(卖票程序)

Thread类:

咱们先不直接调用start方法,越过它,直接去调用run()方法。

package com.wuk.threadAndRunnable;
/**
* 通过卖票来说清楚Thread和Runnable的区别
* @author Administrator
*
*/
public class ThreadTicket extends Thread{

private int ticket=10;

@Override
public void run() {

for(int i=1;i<=10;i++){

if(ticket>0){

System.out.println("票在卖第"+ticket--+"张");
}
}

}

public static void main(String[] args) {

ThreadTicket t1=new ThreadTicket();
ThreadTicket t2=new ThreadTicket();
ThreadTicket t3=new ThreadTicket();

t1.run();
t2.run();
t3.run();
}
}


结果:

票在卖第10张
票在卖第9张
票在卖第8张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张
票在卖第10张
票在卖第9张
票在卖第8张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张
票在卖第10张
票在卖第9张
票在卖第8张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张


看到结果没,很规律,没有涉及到CPU的切换问题,就是咱们正常的代码执行逻辑,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动start()方法启动线程:

package com.wuk.threadAndRunnable;
/**
* 通过卖票来说清楚Thread和Runnable的区别
* @author Administrator
*
*/
public class ThreadTicket extends Thread{

private int ticket=10;

@Override
public void run() {

for(int i=1;i<=10;i++){

if(ticket>0){

System.out.println("票在卖第"+ticket--+"张");
}
}

}

public static void main(String[] args) {

ThreadTicket t1=new ThreadTicket();
ThreadTicket t2=new ThreadTicket();
ThreadTicket t3=new ThreadTicket();
t1.start();
t2.start();
t3.start();

}
}


结果:

票在卖第10张
票在卖第10张
票在卖第10张
票在卖第9张
票在卖第9张
票在卖第8张
票在卖第9张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张
票在卖第8张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张
票在卖第8张
票在卖第7张
票在卖第6张
票在卖第5张
票在卖第4张
票在卖第3张
票在卖第2张
票在卖第1张


这时候看到的结果是杂乱无章的,为什么,因为涉及到CPU的切换调度问题了。这样程序可以正常完成交互式运行。有人就会问,那么为啥非要使用start();方法启动多线程呢?

因为start()用来启动一个线程,当调用start()方法时,系统才会开启一个线程,通过Thead类中start()方法来启动的线程处于就绪状态(可运行状态),此时并没有运行,一旦得到CPU时间片,就自动开始执行run()方法。此时不需要等待run()方法执行完也可以继续执行下面的代码。run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接调用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。

再看Runnable接口:

在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。

package com.wuk.threadAndRunnable;
/**
* 通过卖票来说清楚Thread和Runnable的区别
* @author Administrator
*
*/
public class RunnableTicket implements Runnable{

private int ticket=10;

@Override
public void run() {

for(int i=1;i<=10;i++){

if(ticket>0){

System.out.println("票在卖第"+ticket--+"张");
}
}

}

public static void main(String[] args) {

RunnableTicket t1=new RunnableTicket();

new Thread(t1,"t1").start();
new Thread(t1,"t2").start();
new Thread(t1,"t3").start();
}

}


结果:

票在卖第10张
票在卖第8张
票在卖第9张
票在卖第6张
票在卖第7张
票在卖第4张
票在卖第5张
票在卖第2张
票在卖第3张
票在卖第1张


但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):

同时看到没,达到了资源共享的目的。而继承Thread没有这种效果。

两种实现方式的区别和联系:

在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:

(1)避免点继承的局限,一个类可以继承多个接口。

(2)适合于资源的共享

Runnable接口和Thread之间的联系:

看源码:

public class Thread extends Object implements Runnable


发现Thread类也是Runnable接口的子类。

可见, 实现Runnable接口相对于继承Thread类来说,有如下显著的好处:

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。

(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。

(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

参考:http://www.jb51.net/article/105487.htm,加入了一些自己的感悟。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: