创建并启动一个java线程的时候会发生什么

287 阅读3分钟

1、通过Thread创建线程

// 直接使用Thread类
Thread t1 = new Thread(){
    @Override
    public void run(){
        log.debug("hello");
    }
};
t1.start();
  • 通过构造方法创建线程
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
  • 调用init()方法进行初始化
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}
  • 继续init()方法套娃,初始化线程组、是否为守护线程、target这些属性
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
     // 如果没有自定义name,会调用nextThreadNum()为线程生成名称             
    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 doesn't have a strong opinion of 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(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();
    // 赋值target属性
    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 */
    tid = nextThreadID();
}
  • 调用start()方法启动线程
public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
     // threadstatus=0对应着线程的NEW状态,如果不是new,说明该线程已经启动过,不能多次启动
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}
  • 调用start0()方法
private native void start0();

这是一个native方法,就是把线程的创建交给操作系统去完成了。

  • 调用run()方法 run()方法是任务的执行体,一般把需要执行的任务内容通过重写run()方法去实现
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

在这里可以看到,如果有传入一个Runnable对象,就会调用Runnable的run()方法,否则就执行Thread类自身的run()方法。 至此,一个线程的创建和启动就完成了。

2、通过Runnable+Thread创建线程

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        log.debug("t2 running..");
    }
};
Thread t = new Thread(runnable);
t.start();

流程跟通过Thread创建线程一样,但是有两点不同

  • 通过Runnable+Thread创建线程,把任务和线程分开了,也就是Thread负责创建线程,任务的执行交给Runable
  • 由于Thread的构造方法传入了Runnable作为target,所以会执行target的run()方法,而不是Thread本身的run()方法

使用Runnable创建线程的好处

用Runnable更容易与线程池等高级API配合,因为线程池的核心思想就是生产者和消费者模型,线程和任务解耦

3、通过Callable+FutureTask+Runnable创建线程

// 使用FutureTask配合Callable、Thread
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        log.debug("ts running");
        Thread.sleep(3000);
        return 100;
    }
});
Thread t3 = new Thread(task,"t3");
t3.start();
log.debug("{}",task.get());

这种方式更前两种的最大不同就是,通过Callable创建线程可以获得一个返回结果,而FutureTask包装了这个返回结果,可以通过get()方法得到线程的执行情况

  • FutureTask实现了Runnable接口
public class FutureTask<V> implements RunnableFuture<V>
  • Callable接口的call()方法具有返回值
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
  • 通过向FutureTask的构造方法传入Callable对象创建对象
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}
  • FutureTask对象中的run()方法会调用Callable的call()方法执行任务
  • FutureTask的get()方法会阻塞等待线程执行完成
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}