本文主要是介绍java实现线程的三种方式, stop()和suspend()方法为何不推荐使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 1 线程的实现
- 1.1 继承Thread类
- 1.2 实现Runnable类
- 1.3 继承和实现区别
- 1.4 线程池写法
- 2 stop和suspend方法
1 线程的实现
java5以前,有如下两种:
有两种实现方法,分别使用new Thread()
和new Thread(runnable)
形式,第一种直接调用thread的run
方法,所以,我们往往使用Thread
子类,即new SubThread()
。第二种调用runnable
的run
方法。分别是继承Thread类与实现Runnable接口
其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread
类或其子类的实例对象。每个Thread
对象描述了一个单独的线程。要产生一个线程,有两种方法:
- 需要从
Java.lang.Thread
类派生一个新的线程类,重载它的run()
方法; - 实现
Runnalbe
接口,重载Runnalbe
接口中的run()
方法。
为什么Java要提供两种方法来创建线程呢?它们都有哪些区别?相比而言,哪一种方法更好呢?
在Java
中,类仅支持单继承,也就是说,当定义一个新的类的时候,它只能扩展一个外部类。这样,如果创建自定义线程类的时候是通过扩展Thread
类的方法来实现的,那么这个自定义类就不能再去扩展其他的类,也就无法实现更加复杂的功能。因此,如果自定义类必须扩展其他的类,那么就可以使用实现Runnable
接口的方法来定义该类为线程类,这样就可以避免Java单继承所带来的局限性
1.1 继承Thread类
new Thread(){}.start()
;这表示调用Thread
子类对象的run
方法,newThread(){}
表示一个Thread
的匿名子类的实例对象,子类加上run
方法后的代码如下:
new Thread(){public void run(){//代码}
}.start();
那么为啥非要使用start();
方法启动多线程呢?
在JDK
的安装路径下,src.zip
是全部的 java
源程序,通过此代码找到Thread
中的start()
方法的定义,可以发现此方法中使用了private native void start0();
,其中native
关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI
技术(java Native Interface
)
1.2 实现Runnable类
new Thread(newRunnable(){}).start()
;这表示调用Thread
对象接受的Runnable
对象的run
方法,newRunnable(){}
表示一个Runnable
的匿名子类的实例对象,runnable
的子类加上run
方法后的代码如下:
new Thread(newRunnable(){public void run(){//代码} }).start();
在使用Runnable
定义的子类中没有start()
方法,只有Thread
类中才有。此时观察Thread
类,有一个构造方 法:public Thread(Runnable targer)
此构造方法接受Runnable
的子类实例,也就是说可以通过Thread
类来启动Runnable
实现的多线程。(start()
可以协调系统的资源):
1.3 继承和实现区别
在程序开发中只要是多线程肯定永远以实现Runnable
接口为主,因为实现Runnable
接口相比
继承Thread
类有如下好处:
- 避免点继承的局限,一个类可以继承多个接口。
- 适合于资源的共享
不共享资源写法,利用扩展Thread
类创建的多个线程,虽然执行的是相同的代码,但彼此相互独立,且各自拥有自己的资源,互不干扰
class MyThread extends Thread{private int ticket=10;public void run(){for(int i=0;i<20;i++){if(this.ticket>0){System.out.println("卖票:ticket"+this.ticket--);}}}
};下面通过三个线程对象,同时卖票:
public class ThreadTicket {public static void main(String[] args) {MyThread mt1=new MyThread();MyThread mt2=new MyThread();MyThread mt3=new MyThread();mt1.start();//每个线程都各卖了10张,共卖了30张票mt2.start();//但实际只有10张票,每个线程都卖自己的票mt3.start();//没有达到资源共享}
}
如果用Runnable就可以实现资源共享,下面看例子:
class MyThread implements Runnable{private int ticket=10;public void run(){for(int i=0;i<20;i++){if(this.ticket>0){System.out.println("卖票:ticket"+this.ticket--);}}}
}public class RunnableTicket {public static void main(String[] args) {MyThread mt=new MyThread();new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常new Thread(mt).start();//new Thread(mt).start();}};
结果正如前面分析的那样,程序在内存中仅创建了一个资源,而新建的三个线程都是基于访问这同一资源的,并且由于每个线程上所运行的是相同的代码,因此它们执行的功能也是相同的。
可见,如果现实问题中要求必须创建多个线程来执行同一任务,而且这多个线程之间还将共享同一个资源,那么就可以使用实现Runnable
接口的方式来创建多线程程序。而这一功能通过扩展Thread
类是无法实现的
实现Runnable
接口相对于扩展Thread
类来说,具有无可比拟的优势。这种方式不仅有利于程序的健壮性,使代码能够被多个线程共享,而且代码和数据资源相对独立,从而特别适合多个具有相同代码的线程去处理同一资源的情况。这样一来,线程、代码和数据资源三者有效分离,很好地体现了面向对象程序设计的思想。因此,几乎所有的多线程程序都是通过实现Runnable
接口的方式来完成的
1.4 线程池写法
Java线程池和callable写法深入讲解
Spring线程池ThreadPoolTaskExecutor的使用
2 stop和suspend方法
用synchronized
关键字修饰同步方法
反对使用stop()
,是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
suspend()
方法容易发生死锁。调用suspend()
的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"
的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源
,就会造成死锁。所以不应该使用suspend()
,而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
这篇关于java实现线程的三种方式, stop()和suspend()方法为何不推荐使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!