Java线程的三种创建方式及源码分析

线程的创建主要有三种方式

Thread直接创建

Thread类中有run()方法,每次线程调用start()时,线程启动并开始执行run()方法。因此可以通过重写Thread类中的run()方法来实现线程。

Thread thread = new Thread(){
    @Override
    public void run() {
        System.out.println("t1 run...");
    }
};
thread.setName("t1");
thread.start();

System.out.println("main run...");

Runnable接口继承实现线程

Runnable runnable = () -> System.out.println("t1 run...");

new Thread(runnable, "t1").start();

System.out.println("main run....");

Runnable接口实现线程源码查看

首先,根据上述代码可以看到,在main()方法中通过向Thread类传入继承了Runnable接口的类来实现线程,然后根据这个,找到在Thread类中运行的代码

public Thread(Runnable target, String name) {
    this(null, target, name, 0);
}

通过Thread类中的源码发现,在该构造方法中调用了另一个构造方法,其中Runnable对象被传入到另一个构造方法的第二个参数的位置。然后追踪该被调用的构造方法,如下:

public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    this(group, target, name, stackSize, null, true);
}

依然可以看到该构造方法又调用了另一个构造方法,而Runnable对象依然在被调用的构造方法的第二个参数位置。继续追踪被调用的构造方法,如下:

private Thread(ThreadGroup g, Runnable target, String name,
               long stackSize, AccessControlContext acc,
               boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
               what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security manager doesn't have a strong opinion
               on the matter, use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
    g.checkAccess();

    /*
         * Do we have the required permissions?
         */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(
                SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    this.tid = nextThreadID();
}

在上述代码的 第53行可以看到这样一行代码:this.target = target;,由此可知,该构造方法中将Runnable的对象赋给了类中的属性。然后通过搜索Thread类中的target属性,发现了Thread类中的run()方法(其中线程创建时,当Thread类的start()方法调用后,会自动执行Thread类中的run()方法),而run()方法的源码如下:

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

通过上述代码可以看出,在Thread类中的run()方法中,首先判断target是否存在,即Runnable对象是否存在,如果存在,则调用Runnable对象的run()方法,而Runnable接口的源码如下:

@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

因此,通过Runnable接口创建线程时,需要实现Runnable接口中的Run()方法。虽然在线程创建的时候执行的是Thread类中的run()方法,但实际调用的还是Runnable接口实现的run()方法。

FutureTask类实现

FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        System.out.println("t1 run ....");
        return 100;
    }
});

new Thread(task, "t1s").start();

int num = task.get();

System.out.println("main run" + num);

注意:

  1. 其中FutureTask类实现的线程方式是可以获取该线程的返回值的,获取方式如上代码通过task.get()方式。如果FutureTask创建的线程需要获取返回值,但线程未运行完,即线程还未返回值时,以上述代码为例,其中main线程会在获取返回值的位置,即上述代码中的task.get()处等待,当main线程获取到返回值时,才继续向下运行。
  2. 其中FutureTask方式创建的线程有返回值,而ThreadRunnable方式创建的线程没有返回值。

FutureTask线程实现源码查看

同样,线程的实现都需要调用Thread类,根据这个追踪Thread类的构造函数。源码如下:

public Thread(Runnable target, String name) {
    this(null, target, name, 0);
}

看到这的时候发现通过FutureTask实现线程的方式与通过Runnable接口实现线程的方式在Thread类中的调用方式相同。但在上述Thread类的构造方法中传入的是Runnable接口实现的对象,而在main方法中又可以看出在Thread类中传入的是FutureTask类。

FutureTask类与Runnable接口是什么关系呢?根据这个疑问查看FutureTask类的源码:

public class FutureTask<V> implements RunnableFuture<V> {
    // ......
}

根据上述代码,可以看到FutureTask类并没有实现Runnable接口,但实现了RunnableFuture接口。(猜想:RunableFuture接口继承了Runnable接口),根据这个,再查看RunnableFuture接口的源码, 如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {

    void run();
}

根据上面的代码是可以确认RunnableFuture接口是继承Runnable接口的。

通过上面的分析,可以知道为什么FutureTask类的实例可以传入到Thread类的实例中。但在通过FutureTask类实现的线程中,实现了Callable接口并通过实现call()方法来实现该线程的逻辑,这是什么原因?FutureTask类实现的线程又是如何返回值的。

通过之前的源码分析,可以知道,Thread中的run()方法调用的实际上是Runnable接口的实现类的run()方法。同时FutureTask类实现了继承Runnable接口的RunnableFutrue接口,那么FutureTask必然实现run()方法,接下来查看FutureTask类中的run()方法实现,源码如下:

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);
        }
    } 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);
    }
}

通过上面代码的第6行:Callable<V> c = callable;可以看到将FutureTask类中的属性callable赋给了c,并且在11行调用了call()方法,并返回了结果给result,第19行又执行了set(result)代码。由此产生以下几个疑问:

  1. FutureTask类中的callable属性的值或对象的实例是如何传进来的?(联想main()方法中线程实现时在FuturtTask类内传入了实现的Callable接口的实例)。
  2. FutureTaskrun()方法并没有返回值,那通过FutureTask类实现的线程又是如何返回值的?(联想main()方法中获取线程的返回值是通过FutureTask类的实例调用get()方法)

首先解决第一个疑问,callable属性是如何获得值的。在main()方法中传入了Callable接口的实例,根据这个查看FutureTask的源码,如下:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

如代码所示,FutureTask类直接通过构造方法将Callable类的实例传给callable属性。

解决第二个问题,如何将线程的值返回。在FutureTask类中的run()方法有三行代码Callable<V> c = callable;result = c.call();set(result);,再根据main()方法获取线程的返回值是通过get()方法,可以看出result就是返回值,call()Callable接口的方法。其中Callable接口源码如下:

@FunctionalInterface
public interface Callable<V> {

    V call() throws Exception;
}

set(result)代码是将返回值结果存储到了FutureTask的属性中,可以追踪FutureTask类中set()方法的源码,如下:

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

可以看到,set()方法中将返回值赋给了FutureTask类的属性outcome。联想到main()方法中通过get()方法获取线程返回值。然后查看FutureTask类中的get()方法,代码如下:

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

可以看到最终返回的是report(s)的方法,继续追踪到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);
}

可以看到,方法中将outcome属性的值赋给x, 然后将x返回出去。最总在main()方法中获得返回的值。

FutureTask的类图如下:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值