创建线程的四种方式:Thread、Runnable、Callable、线程池

1、继承Thread类,覆盖重写run方法

public class MyThread extends Thread{
	@Override
	public void run() {
		System.out.println("创建线程的第一种方法:继承Thread类");
	}
}

2、实现Runnable接口,覆盖重写run方法

		Runnable r=new Runnable() {
			@Override
			public void run() {
				System.out.println("创建线程的第二种方式:实现Runnable接口");
			}			
		};
		new Thread(r).start(); //将r注入线程,并启动

3、实现Callable接口,覆盖重写call方法

万变不离其宗,创建线程本质上还是要创建Thread类。
可以通过无参构造创建(方法1),也可以通过有参构造,传入Runnable类型的对象(方法2)

Thread t =new Thread(); 

那么Runnable和Callable有什么联系呢???
简单画了个图表示他们之间复杂的关系。总的来说就是:
(1)要构造Thread,就要传入Runnable类型的对象;
(2)FutureTask<>间接继承实现了Runnable接口,所以可以传入FutureTask<>;
(3)要构造FutureTask<>,就要传入Callable类型的对象;
(4)Callable是一个接口不能实例化,所以可以传入Callable实现类的对象。
(可以用匿名内部类实现Callable接口)
在这里插入图片描述
代码就是这样写:

Callable<String> c=new Callable<String>() {
			@Override
			public String call() throws Exception {
				System.out.println("创建线程的第三种方法:实现Callable接口");
				return "返回"; //返回到哪,一会再说
			}	
		};
		FutureTask<String> ft=new FutureTask<String>(c);
		new Thread(ft).start();		

这样,我们调用线程的strat()方法就可以运行run方法了。
但是,run()在哪呢???
回想一下,Runnable方法的run被我们覆盖重写了,FutureTask类型的ft里面应该也有run方法,这样我们启动线程的时候才能做任务。那我们去看看FutureTask里的源码找找run方法:(第254行)

public void run() {
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call(); //!!!!!!!!!!!!!!!!!
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);//!!!!!!!!!!!!!!
            }

可以看到,run方法里面接收了call的返回值并赋值给result,最后调用set方法传入了result
看看set是啥:

protected void set(V v) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            outcome = v; //!!!!!!!!!!!!!!!!!!!!!!
            STATE.setRelease(this, NORMAL); // final state
            finishCompletion();
        }
    }

set里面传入的泛型v起始就是result,把这个result赋值给outcome。outcome是FutureTask类的成员变量。
再拓展一下,由set就有get,那我们去源码里找找get,看看get做了啥:

 public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s); //!!!!!!!!!!!!!!!!!!!!!1
    }

get的返回值是泛型,调用了report方法,那再看看report:(快结束了再坚持一下……)

private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x; //!!!!!!!!!!!!!!!!!!!!!!!!!!!
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

report的返回值也是泛型,返回x,x就是outcome!!!
好像串起来了!!!
在这里插入图片描述
也就是说,我们以前用Runnable方法时传入的对象r,这个r没有什么特有的方法了。
但是我们用FutureTask间接继承实现的Runnable方法传入的对象ft有很多自带的方法,有run方法、set方法、get方法等等,这个get方法可以获取传入FutureTask中Callable对象的返回值(晕)
我们把试一下get方法:

Callable<String> c=new Callable<String>() {
			@Override
			public String call() throws Exception {
				System.out.println("创建线程的第三种方法:实现Callable接口");
				return "实现接口";
			}			
		};
		FutureTask<String> ft=new FutureTask<String>(c);
		Thread t=new Thread(ft);
		t.start();
		System.out.println(ft.get());

输出结果是:
在这里插入图片描述

4、线程池创建

其实也是Runnable方法,让线程池中的线程执行

Runnable r=new Runnable() {
			@Override
			public void run() {
				System.out.println("创建线程的第四种方法:线程池创建");
			}		
		};
		ExecutorService es=Executors.newFixedThreadPool(2);
		es.execute(r);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值