就操作系统而言,民用系统最早可用DOS,但是传统的DOS系统有一个特点:电脑病毒出现后,系统会死亡,因为传统DOS该系统采用单过程处理方法。windows在时代,采用多过程处理方法,多个程序将在同一时间段并发执行,轮流强占CPU资源。
然而,过程的启动和破坏仍然非常缓慢。因此,后来人们开始尝试进一步优化过程,从而产生了线程的概念,即线程是在过程的基础上扩展的。线程的启动和销毁将比过程更快。多个线程可以在一个过程中划分,如果过程消失,线程也会消失,Java支持多线程编程的少数语言之一
如果你想实现多线程的开发,你必须像主类一样需要一个线程的主类,但这个类不能单独定义,必须继承Thread类或实现Runnable接口(如果从实际开发来看,必须是Runnable接口)。 1. 继承Thread类 线程的主要类别只需要通过extends 关键字继承Thread类,然后这个类可以用于线程控制,当继承时Thread用户还需要重复Thread类之中的run()方法“public void run)”。 例子:定义线程的主题类别
class MyThread extends Thread{ private String name; ////理解为对象的名称 public MyThread(String name){ this.name = name; } @Override public void fun(){ ///主线程方法 for(int x = 0 ; x < 10 ; x ){ System.out.println(this.name ", x= " x); } } }
此时每一个MyThread类的对象是一个线程,因此可以产生多个线程。
但是,当线程主体类定义完成后,必须通过对象访问,但调用的不是类中runO方法,而是Thread类的start()方法:public void start)。调用此方法相当于调用run()方法。 例子:启动多线程。
public class TextDemo { public static void main(String[] args){ MyThread mtA = new MyThread("线程A"); MyThread mtB = new MyThread("线程B"); MyThread mtC = new MyThread("线程C"); mtA.start(); mtB.start(); mtC.start(); } }
若使用runO事实上,该方法只是一种普通的调用方法,将按顺序执行,如果调用是start()方法意味着将启动多线程,多线程并发运行。 问题:通过简短的分析,我们应该已经知道多线程的基本运行过程,但为什么要通过呢?start()来调用run()呢? 为便于理解,打开关于Thread类中startO定义方法:
public synchronized void start(){ if(threadstatus != 0) throw newIllegalThreadstateException(); group.add(this); boolean started = false; try { start0(); started = true;. }finally { try { if (!started) { group.threadStartFailed(this); } }catch (Throwable ignore){ } } }
本方法会抛出一个“IllegalThreadStateException异常,但如果是手工使用的话throw应使用抛出异常try..catch处理是对的。但此时没有强制性要求,因为通过观察IllegalThreadStateException继承结构:
java.lang.Object java.lang.Throwable java.lang.Exception java.lang.RuntimeException java.lang.IllegalArgumentException java.lang.IllegalThreadStateExceptione |
因为RuntimeException子类,通常当一个线程重复启动时就会抛出这种异常。因此,一个线程只能启动一次。
在调用中可以发现start()方法自动调用start()方法,而在start在方法的声明中发现了一个()native关键字,此时关键字表示代码将通过本地原始函数实现。 必须操作线程CPU每个操作系统的资源调度是不同的,所以这种方法实际上是由JVM实现时,只给出抽象方法的名称,具体实现将根据不同的操作系统重写,从而实现可移植性。
因此,当用户执行时,可以得出结论start方法意味着操作系统的资源部署将在部署后实施run()方法,即任何时候只要启动多线程,都必须使用Thread类之中的start()方法。
2.实现Runnable接口. 不建议使用类与类之间的继承,但类实现接口是推荐功能Java为了解决多线程单继承的局限性,还提供了一个Runnable接口,用户只需要让线程的主要类别实现Runnable接口即可。 范例:观察Runnable 界面定义结构.
public interface Runnable{ public void run(); }
范例:使用Runnable定义线程主体类
class MyThread implements Runnable{ private String name; public MyThread(String name){ this.name = name; } public void run(){ ///主线程方法 for(int x = 0 ; x < 10 ; x ){ System.out.println(this.name ", x= " x); } } }
通过之前的分析可以得出结论,只要线程的启动必须依赖于Thread类的start)如果说一个类现在直接继承了方法Thread类,然后就可以继承了start()方法,但此时实现了Runnable接口,那就没有了start可被继承。然后观察下面Thread类中结构方法的定义:public Thread(Runnable target)”。发现可以接收一个Runnable接口子类对象
例子:启动多线程
public class TextDemo { public static void main(String[] args){ MyThread mtA = new MyThread("线程A"); MyThread mtB = new MyThread("线程B"); MyThread mtC = new MyThread("线程C"); new Thread(mtA).start(); new Thread(mtB).start(); new Thread(mtC).start(); } }
此时两者的功能完全是同的,但是很明显,使用Runnable要比使用Thread更加合理。
两种方式实现的区别
通过一系列的分析之后已经清楚了多线程的两种实现方式,但是这两种方式从结构上讲一定使用的是Runnable,是除了这一点之外,还有其它的区别吗? 那么首先来观察一下Thread类的定义:
public class Thread extends Object implements Runnable |
发现原来Thread类也实现了Runnable接口,于是一件很有意思的事情发生了。
从结构上讲Thread是一个代理类的结构设计,但是又不那么完整。如果是一个纯粹的代理设计模式,那么用户应该调用的是Thread类的run()方法,但是现在调用的却是start()方法(并不是Runnable接口提供的方法)所以在整个操作之中虽然形式是代理结构,但是最终还是有差异的,而这个差异也是由于历史原因造成的。 除了这个基本的联系之外,还有一点不算区别的小区别:使用Runnable接口实现的多线程多线程要比使用Thread类实现的多线程更容易表示出数据共享的概念。 范例:编写一个简单的卖票程序,利用Thread类实现(产生三个线程对象一起卖票)
package cn.mldn.demo;
class MyThread extends Thread{
private int ticket = 5;
public void run(){ //线程的主方法
for(int x = 0 ; x < 20 ; x++ ){
if(this.ticket > 0){
System.out.println("卖票:ticked = " + this.ticket--);
}
}
}
}
public class TextDemo {
public static void main(String[] args){
new MyThread().start() ; //一个线程
new MyThread().start() ; //二个线程
new MyThread().start() ; //三个线程
}
}
此时发现每个线程对象都有五张票,不符合要求
范例:利用Runnable接口实现
package cn.mldn.demo;
class MyThread implements Runnable{
private int ticket = 5;
public void run(){ //线程的主方法
for(int x = 0 ; x < 20 ; x++ ){
if(this.ticket > 0){
System.out.println("卖票:ticked = " + this.ticket--);
}
}
}
}
public class TextDemo {
public static void main(String[] args){
MyThread mt = new MyThread();
new Thread(mt).start() ; //一个线程
new Thread(mt).start() ; //二个线程
new Thread(mt).start() ; //三个线程
}
}
请解释出多线程两种实现方式的区别?
- 多线程的两种实现方式:继承Thread类、实现Runnable接口;
- 如果继承了Thread类,那么会受到单继承局限,而且不方便表示出数据共享的概念,Thread类是Runnable接口的子类;
- 如果实现了Runnable接口,那么将不受到单继承的影响,同时可以方便的表示出数据共享操作
- 但是不管使用何种方式,最终一定要通过Thread类的 startO方法才可以启动多线程。