JAVA虚拟机允许程序并发地执行多个线程,那如何去创建一个线程呢?
方法一:继承Thread类
1、将类声明为Thread的子类,该子类应重写Thread类的run()方法,将想要执行的代码写入run()方法,然后就可以分配并启动该子类的实例。
2、创建线程的子类对象,并调用该对象的start()方法。调用start()方法,Java虚拟机会默认调用该线程的run()方法。
示例一:
package com.mec.thread;
public class TestExtendsThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("这是重写的run()方法!");
}
}
}
package com.mec.thread;
public class TestExtendsThreadDemo {
public static void main(String[] args) {
TestExtendsThread tet = new TestExtendsThread();
tet.start();
}
}
运行结果:
方法二:实现Runnable接口
创建线程的另一种方法是声明实现Runnable接口的类,重写该接口的方法。然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动。
示例二:
第一步:声明实现Runnable接口的类,该类实现run()方法
package com.mec.thread;
public class AboutThread implements Runnable{
@Override
public void run() {
for(int i = 0; i < 1000000; i++){
System.out.println("这是AboutThread implements Runnable类的run()方法的输出");
}
}
}
第二步:测试类中创建线程,并以Runnable接口实现类的对象为参数
package com.mec.thread;
public class AboutThreadTest extends Thread{
public static void main(String[] args) {
AboutThread at = new AboutThread(); //at是AboutThread类的对象,作为参数传递
Thread thread = new Thread(at);
thread.start();
for(int i = 0; i < 100000; i++){
System.out.println("--------主线程--------------------");
}
}
}
运行结果:
两种实现方法的区别是
在源码层;
方法一:子类重写了Thread类的run()方法,当调用start()方法时,直接调用子类的run()方法。
方法二:这种方式将Runnable接口的实现类对象(即示例中 AboutThread类的对象at)作为参数传递给Thread类的构造方法,便于Thread类内部源码的使用,使得编译是看的是Runnable接口的run()方法,而执行的时候直接执行子类的run()方法。
*下面我们来看具体的内部原理:
1、
这是在Thread类的内部: 首先、Runnable接口的对象作为参数传递给了Thread的构造函数,执行构造函数内部的init()方法。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
2、我们再来看init()方法:
private void init(ThreadGroup g, Runnable target, String name,long stackSize){
.......
this.target = target;
.......
}
3、它把传递进来的参数Rannable target赋值给了这个Thread类本身的Runnable target成员。接下来,也是最关键的地方是:
@Override
public void run() {
if (target != null) {
target.run();
}
}
Thread类的run()方法调用了target的run()方法,而target是Runnable接口的实现类对象,它的run方法实际就是Runnable接口的run()方法!所以,方法二创建线程在编译时实际执行的是Runnable接口的run(),运行时执行的是子类的run()。
证明:
package com.mec.thread;
public class Test {
public static void main(String[] args) {
new Thread(new Demo1()){
public void run(){
System.out.println("执行");
}
}.start();
}
}
class Demo1 implements Runnable{
@Override
public void run() {
System.out.println("Runnable");
}
}
输出结果:
输出很明显证明了运行时的run()是Thread对象的run()方法而不是Runnable接口的run()方法。
方式三、实现Callable接口
这种实现方式和实现Runnable接口的用法一致,不同的是:Callable的实现类中重写其call()方法,可以进行计算并返回一个计算结果,这个结果用Future类的对象来接受,通过get方法可以得到其值。
并且这种实现方式支持抛出异常,而前两种方式不支持抛出异常。因为Thread类也是实现了Runnable接口,在Runnable接口中并没有抛出异常,Thread的子类或Runnable接口的实现类不能抛出父类或接口中没有的异常。
上面是callable接口
Runnable接口