Java多线程—— 线程的3种创建方式

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接口


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值