JAVA 创建线程的三种方式 源码解析

JAVA 创建线程的三种方式 源码解析,第1张

JAVA 创建线程的三种方式 源码解析 JAVA 创建线程的几种方式 源码解析

    继承Thread类

    实现Runnable接口

    Future + Callable

原理简介:任何一个线程的开启,就是调用Thread实例的start( ) 方法,然后JVM新起一个线程执行Thread实例的 run( ) 方法。

先研究start( ) 方法的源码

 public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0(); // 可以看到,start() 没有直接调用run()方法,而是调用了本地方法start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
	//本地方法,功能是开启一个线程,并执行run()方法
    private native void start0();

再研究run( ) 方法的源码

private Runnable target;
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
    if (target != null) {
        target.run(); //Runnable 实例不为Null,调用Runnable实例的 run()方法。  
    }
}

分析start()和 run()方法后,目前我们可以得出两种创建线程的方式,

一、继承Thread类,重写Thread 的run( )方法。

二、实现Runnable接口,重写Runnable的run()

附上代码

//继承Thread类
public class Test {
    public static void main(String[] args) {
        new MyThread().start();
    }
    private static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("继承Thread类,重写run()方法");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
               System.out.println("实现Runnable接口,重写Runnable的run()"); 
            }
        }).start();
    }
}

当然这两种方法创建线程都没有返回值,如果我们需要有返回值的线程,该如何创建呢?

由于Thread类没有带返回值创建线程的API,所以需要我们自己实现,但是好在JDK1.5 提供了 Future + Callable机制。

那么接下来我们需要研究,在Thread类不支持带返回值创建线程的情况下,Future + Callable机制如何实现。

为了方便大家理解,先贴上Future + Callable 的 API 使用

public class Test {
    public static void main(String[] args) {
        // 用 Future 包装 Callable接口,传递给Thread
        final FutureTask future = new FutureTask<>(new Callable(){
            @Override
            public Integer call() throws Exception {
                Thread.sleep(2000);
                return 1;
            }
        });
        final Thread thread = new Thread(future);
        thread.start();
        try {
            System.out.println("开始获取结果...");
            Integer result = future.get();// 阻塞,直到获取到结果 。
            System.out.println("获取结果成功"+ result);
        } catch (InterruptedException e) {
            e.printStackTrace(); // thread被中断
        } catch (ExecutionException e) {
            e.printStackTrace();  // thread 内部业务逻辑抛出的异常
        }
    }
}

UML图 FutureTask

FutureTask 由于实现了 Runnable接口,所以可以用于构建 Thread对象,那么在开启线程时,将会执行FutureTask里的 run( )方法.

所以创建带有返回值线程的秘密就藏在 FutureTask类里

开搞!

先研究 他的run方法()

public class FutureTask implements RunnableFuture {
	
    private Object outcome; // non-volatile, protected by state reads/writes
    
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();  //执行 Callable接口的 Call方法。
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex); // 将异常信息存储到outcome中
                }
                if (ran)
                    set(result); //将执行结果,放到成员变量 outcome 中
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v; // 将一个值,赋值给成员变量outcome
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    
}

理一下逻辑,FutureTask 类 通过实现 Runnable接口,重写了run( ) 方法。在run( ) 方法中,调用 Callable实例的 call ( ) 方法,并将 call( ) 方法返回值存储到 的成员变量outcome中。如果出了异常,就将异常信息存储在outcome中。这样一来,线程的返回值和异常信息就有地方保存了。

接下来,我们研究如何把这个返回值取出来。 API是调用 Future 的get()方法。我们研究一下源码

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L); //阻塞,直到线程运行完
    return report(s); 
}
//判断是返回结果还是抛出异常
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);
}

不用说大家也猜到了,获取返回值的时候阻塞,直到线程出结果,或者抛出异常

总结:

本质上线程的创建就两种方式,继承Thread类和实现Runnable接口。但是JDK1.5加入了Future + Callable机制 该机制可以创建线程并获取返回值。该机制底层还是通过实现Runnable接口创建线程。 实现原理是 使用FutureTask的成员变量outcome作为中介,存储了线程运算的返回值或者异常信息。然后对外提供get() 方法。当调用get() 方法时会阻塞,直到抛出异常,超时,或者线程运行完毕。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5707775.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存