Java的JDK开发包已经自带了对多线程技术的支持,通过它可以很方便地进行多线程编程.实现多线程编程主要有两种方式:1.继承Thread类. 2.实现Runnable接口.
学习如何创建新的线程前,我们先看看Thread类的声明结构(在Thread类上按住Ctrl键不放,同时点击鼠标左键,进入底层源码):
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
从上面的源码中不难发现,Thread类实现了Runnable接口,他们之间具有多态关系,多态结构的示例代码如下
Runnable run1 = new Thread();
Runnable run2 = new MyThread();
Thread t = new MyThread();
其实使用继承Thread类的方式创建新的线程时,其最大的局限性就是不支持多继承,因为Java语言的特点是单继承,所以为了支持多继承,完全可以实现Runnable接口,即一边实现一边继承,但这两种方式创建的功能是一样的,没有本质的区别.
下面我们主要介绍第一种方式.创建一个java项目,创建一个自定义的线程类MyThread.java,此类继承自Thread,并且重写run()方法,在run()方法中添加线程要执行的任务代码如下:
package com.xxx.thread001;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread.............");
}
}
运行类代码如下:
package com.xxx.thread001;
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//耗时较大,输出较慢
System.out.println("我是第一个运行结束的.....");//耗时较小
}
}
输出结果:
我是第一个运行结束的.....
MyThread.............
Process finished with exit code 0
运行分析
上述代码中使用start()方法来启动一个线程,线程启动后会自动调用线程对象中的run()方法,run()方法里面的代码就是线程对象要执行的任务,也就是线程执行任务的入口.
从输出结果来看,run()方法的执行时间相对与输出"我是第一个运行结束的....."的执行时间滞后,因为start()方法的执行是比较耗时的,这就增加了先输出"我是第一个运行结束的....."的概率.start()方法耗时的原因与执行步骤是分不开的,start()方法的执行步骤如下:
1).通过JVM告知操作系统创建Thraed.
2).操作系统开辟内存并使用Windows SDK中的createThread()函数创建Thread线程对象.
3).操作系统对Thread对象进行调度,并确定执行时机.
4).Thread在操作系统中被成功执行.
当上面4步完整执行后,所消耗的时间一定大于"我是第一个运行结束的....."的时间.另外,main线程执行start()方法时不必等待4步都执行完,而是立即执行start()方法后面的代码,这4步会与"我是第一个运行结束的....."的代码一同执行,由于"我是第一个运行结束的....."耗时较小,所以大多数的情况下,先输出"我是第一个运行结束的.....",后输出"MyThread............."
但在这里,还是有非常非常小的,机会十分渺茫的输出结果是:
MyThread.............
我是第一个运行结束的.....
Process finished with exit code 0
输出这个结果,说明执行完整的start()方法的4步后,才输出"我是第一个运行结束的.....",这也说明线程的执行顺序具有随机性.
总结:
在使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的.另外,线程是一个子任务,CPU以不确定的方式,或者说以随机的时间来调用线程的run()方法,所以说先输出"
MyThread.............",还是先输出"我是第一个运行结束的....."具有不确定性.