从计算机操作系统的发展来看,经历了这样的两个阶段:
单进程处理:最传统的DOS 系统中只要有病毒出现,则立刻有反映,因为在DOS 系统中属于进程处理,即:在同一个时间段上只能有一个程序在执行
多进程处理:windows操作系统是一个多进程,例如,假设在windows 中出现病毒了,则系统照样可以使用
那么对于资源来讲,所有IO设置、CUP等等都只有一个,那么对于多进程的处理来讲,在同一个时间段上会有多个程序运行,但是在同一个时间点上只能有一个程序运行
线程是在进程基础上的进一步划分,举个不恰当的例子来说:word 中的拼写检查,是在word整个程序运行中运行的。
所以,如果进程,则线程就消失,而如果线程消失的话,则进程依然会执行,未必会消失。
Java 本身是属于多线程的操作语言,所以提供了线程的处理机制。
线程实现的两种方式
在Java 中可以有两种方式实现多线程操作,一种是继承Tread类,另外一种是实现Runnable 接口
Thread 类
Thread 类是在java.lang 包中定义
一个类只要继承了Thread类,同时覆写了本类中的run()方法,则就可以实现多线程的操作了
package org.threaddemo;
public class MyThread extends Thread{
private String name; //定义name 属性
public MyThread(String name){
this.name = name;
}
public void run(){ //覆写run()方法
for (int i=0;i<50;i++){ // 表示循环50次
System.out.println("Thread 测试"+this.name+" i:"+i);
}
}
}
以上的类已经完成了多线程的操作类,那么下面就启动线程
package org.threaddemo;
public class ThreadDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread th1 = new MyThread("A类");
MyThread th2 = new MyThread("B类");
th1.run();
th2.run();
}
}
但是,此时的执行可以发现非常的有规律,先执行完第一个对象,再执行第二个对象,也就是说并没有实现交互的运行
从JDK的文档中可以发现,一旦调用strat()方法,则会通过JVM找到run()方法
public void start()
package org.threaddemo;
public class ThreadDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread th1 = new MyThread("A类");
MyThread th2 = new MyThread("B类");
th1.start();
th2.start();
}
}
此时,程序已经可以正常的进行交互式的运行了。
但是,需要思考的是,为什么非要使用start()方法启动多线程呢?
在JDK 的安装路径下,src.zip是全部的java 源程序,通过此代码找到 Thread 类中的 start()方法的定义:
public synchronized void start(){ //定义的start()方法
if (started) //判断线程是否已经启动
throw new IllegalThreadStateException();
started = true;//如果没有启动则修改状态
start0();//调用start0()方法
}
private native void start0(); //使用 natvie 关键字声明的方法,没有方法体
操作系统有很多种,Windows、Linux、UNIX,既然多线程操作中要进行CPU资源的强占,也就是说要等待CPU调度,那么这些调度的操作是由各个操作系统的低层实现的,所以在java程序中根中就没法实现,那么此时Java的设计者定义了native 关键字,使用此关键字表示可以调用操作系统的低层函数,那么这样的技术又称为JNI技术(Java Native Interface)
而且,此方法在执行的时候将调用run()方法完成,由系统默认调用的
但是,第一种操作中有一个最大的限制,一个类只能继承一个父类
Runnable 接口
在实际的开发中一个多线程的操作类很少去使用Thread 类完成,而是通过Runnable 接口完成。
观察Runnable 接口的定义:
public interface Runnable{
public void run();
}
所以,一个类只要实现了此接口,并覆写 run()方法
package org.threaddemo;
public class RunnableDemo implements Runnable{ //实现Runnable 接口
private String name; // 定义name 属性
public RunnableDemo(String name){
this.name = name;
}
public void run(){ //覆写run()方法
for (int i=0;i<50;i++){ //表示循环10次
System.out.println("Thread 测试"+this.name+" i:"+i);
}
}
}
完成之后,下面继续启动多线程
但在现在使用Runnable 定义的子类中并没有start()方法,而只有Thread类中才有
在Thread 类中存在以下的一个构造方法:
public Thread(Runnable target)
此构造方法接收Runnable 的子类实例。也就是说现在可以通过Thread 类中启动 Runnable 实现的多线程
package org.threaddemo;
public class ThreadDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread th1 = new MyThread("A类");
MyThread th2 = new MyThread("B类");
new Thread(th1).start(); //调用线程体
new Thread(th2).start(); //调用线程体
}
}
以上的操作代码也属于交替的运行,所以此时程序也同样实现了多线程的操作
两种实现方式的区别及联系
在程序的开发中只要是多线程则肯定永远以实现Runnable 接口为正统操作,因为实现Runnable 接口相比继承 Thread 类有如下的好处
避免单继承的局限,一个类可以同时实现多个接口
适合于资源的共享
以卖票的程序为例
public class TicketsDemo extends Thread { //继承 Thread
private int TiceketsDemo = 5; // 一共才5张票
public void run(){ //覆写 run()方法
for (int i=0;i<50;i++){ //表示循环50次
if (this.TiceketsDemo>0){
System.out.println("出票:"+this.TiceketsDemo--);
}
}
}
}
下面建立三个线程对象,同时卖票
package org.threaddemo;
public class ThreadDemo03 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketsDemo tic1 = new TicketsDemo(); //一个线程
TicketsDemo tic2 = new TicketsDemo(); //一个线程
TicketsDemo tic3 = new TicketsDemo(); //一个线程
tic1.start(); //开始卖票
tic2.start(); //开始卖票
tic3.start(); //开始卖票
}
}
发现现在一共买了15张票,但是实际上只有5张票。所以证明每一个线程都买自己的票,没有达到资源共享的目的
那么,如果现在实现的是Runnable 接口的话,则就可以实现资源的共享:
package org.threaddemo;
public class ThreadDemo03 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketsDemo tic1 = new TicketsDemo();
new Thread(tic1).start();
new Thread(tic1).start();
new Thread(tic1).start();
}
}
虽然现在程序中有三个线程,但是三个线程一共才卖出了5张票。也就是说使用Runnable 实现的多线程可以达到资源共享的目的。
实际上 Runnable 接口和Thread 类之间还是存在联系的:
public class Thread extends Object implements Runnable
发现Thread 类也是Runnable 接口的子类