
虽然Thread类可以实现多线程的主体类定义,但是它有一个问题,java具有单线程局限,正因如此针对于类的继承都应该是回避的问题,那么多线程也一样。为了解决单线程的限制,在java里面专门提供了Runnable接口。
1、实现Runnable接口方式实现多线程与继承Thread类相比,实现Runnable接口的RunnableTest类在结构上与之前是没有区别的,但是有一点是有严重区别的,如果继承了Thread类,那么可以直接继承start()方法,但是实现Runnable接口是没有start()方法的。不管何种情况下,如果要启动多线程一定要依靠Thread类完成,多线程唯一的启动方法就是Thread类中的start方法。
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口,
写一个类实现Runnable接口,实现run方法。用new Thread(Runnable target).start()方法来启动,如下:
1.1定义Runnable接口的实现类
class RunnableTest implements Runnable{
private int ticket =10;
//1.2重写其中的run方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (this.ticket > 0) {
System.out.println("票号:"+ticket-- + ",已被售卖,售卖窗口:" + Thread.currentThread().getName());
}
}
}
}
1.1、线程独立完成任务
生成3个任务对象实例,每个任务生成独立的线程来执行:即3个卖票10张的任务分别分给3个窗口,他们各做各的事各卖各的票各完成各的任务。
RunnableTest rt1=new RunnableTest(); RunnableTest rt2=new RunnableTest(); RunnableTest rt3=new RunnableTest(); Thread t1 = new Thread(rt1,"1号窗口"); Thread t2 = new Thread(rt2,"2号窗口"); Thread t3 = new Thread(rt3,"3号窗口"); t1.start(); t2.start(); t3.start();
执行结果:
【线程的创建和启动的步骤(实现Runnable接口方式)】
1.定义Runnable接口的实现类,并重写其中的run方法。run()方法的方法体是线程执行体。
2.创建Runnable接口实现类的实例。 RunnableTest rt1 = new RunnableTest();
3.用该实例作为Thread的target来创建Thread对象。 Thread t1=new Thread(rt1,"1号窗口");
4.调用该对象的start()方法启动线程。 t1.start();
【关于命名】
在创建Thread 对象的时候可以为该Thread对象指定一个名字
Thread t1=new Thread(rt1,"1号窗口");
【Thread的构造方法】
Runnable对象作为Thread对象的target,Runnable实现类里面包含run方法仅仅作为执行体。也就是说Thread类的作用是把run方法包装成线程的执行体。
实际运行的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run方法。
(再不考虑线程安全的情况下,学习的例子)
实际开发中我们通常采用Runnable接口来实现多线程。因为实现Runnable接口比继承Thread类有如下好处:
1. 避免继承的局限,一个类可以继承多个接口,但是类只能继承一个类。
2. Runnable接口实现的线程便于资源共享。而通过Thread类实现,各自线程(继承子类创建的对象线程)的资源是独立的,不方便共享。
public class RunnableDemo {
public static void main(String[] args){
//2.创建Runnable的接口实现类的实例
RunnableTest rt1 = new RunnableTest();
//2.用Runnable的接口实现类的实例作为Thread的target,创建Thread对象,同时为之命名
Thread t1=new Thread(rt1,"1号窗口");
Thread t2=new Thread(rt1,"2号窗口");
Thread t3=new Thread(rt1,"3号窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
执行结果:
实现资源共享:编写一个任务类实现Runnable接口, 相当于是拿出一个卖票10张的任务给三个人去共同完成,new RunnableTest相当于创建一个任务,然后通过new Thread实例化3个Thread,创建3个线程即1个任务安排3个窗口去执行。
实现Runable的一个好处就是能实现资源共享。当然实现一个任务用多个线程来做也可以用继承Thread类来实现只是比较麻烦,一般我们用实现Runnable接口来实现,简洁明了。
大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应建议使用 Runnable 接口。
其他摘抄信息:
实现Runnable接口相对于继承Thread类来说,有如下显著的好处:
1.适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想。
2.可以避免由于JAVA的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。
3.有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以 *** 作相同的数据,与它们的代码无关。当共享访问相同的对象时,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。
事实上,几乎所有多线程应用都可以用实现Runnable接口的方式。-----------------------------
对于具有相同目标对象的线程,当其中一个线程享用CPU资源时,目标对象自动调用接口中的run()方法,这时run()方法中的局部变量被分配内存空间。当轮到另一个线程享用CPU资源时,目标对象会再次调用接口中的run()方法,那么run()方法中的局部变量会再次分配内存空间。也就是说,run()方法已经启动运行了两次,分别运行在不同的线程中,即运行在不同的时间片内。run()方法中的局部变量称为线程的局部变量。不同线程的run()方法中的局部变量互不干扰,一个线程改变了自己的run()方法中局部变量的值,不会影响其他线程的run()方法中的局部变量。
面试题:请解释Thread类和Runnable接口实现多线程的区别?(请解释多线程两种实现方式的区别?)
Thread类是Runnable接口的子类,使用Runnable接口实现多线程可以避免单继承局限。
Runnable接口实现的多线程可以比Thread类实现的多线程更加清楚的描述数据共享的概念。
参考:java学习线程篇之多线程的实现方式:继承Thread类、实现Runnable接口或Callable接口
https://blog.csdn.net/qq_34474324/article/details/100035644
参考:https://www.cnblogs.com/echo-cheng/p/6814909.html
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)