1 类继承体系

Runnable 接口只有一个核心方法:

public abstract void run();

子类通过继承 Thread 并重写 run 方法实现自己的业务逻辑。

2 初始化

当我们使用 new Thread() 的方法创建新的线程时,会调用init方法:

  1. ThreadGroup g:线程组,如果没有指定线程组,那么默认的线程组就是父线程的线程组,比如说如果你的父线程是main线程的话,那么你的线程组就是main线程的线程组(main线程组)。
  2. Runnable target:目标
  3. name:线程名字
  4. stackSize:默认是0。它的作用是控制jvm给线程分配栈内存的大小。如果这个值比JVM规定的最小值还小的话,取JVM的默认值,一般是1M。
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

默认情况下,如果你没有指定你是否为daemon的话,那么你的daemon的状态是由父线程决定的,就是说如果你的父线程是daemon线程,那么你也是daemon线程;同理,你的优先级如果没有指定的话,那么就跟父线程的优先级保持一致。

private void init(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) {
        if (security != null) {
            g = security.getThreadGroup();
        }
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }
    g.checkAccess();
    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();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    this.stackSize = stackSize;
  	// 每个线程其实都有一个线程id,threadId,第一个分配的线程,它的id是1,之后的线程是2,3,4,5,依次分配各个线程的id
    tid = nextThreadID();
}

3 初始化总结

  1. 创建 A 线程的线程,就是 A 线程的父线程

  2. 如果没有指定ThreadGroup,A 线程的ThreadGroup就是父线程的ThreadGroup

  3. A 线程的daemon状态默认是父线程的daemon状态

  4. A 线程的优先级默认是父线程的优先级

  5. 如果没有指定线程的名称,那么默认就是 Thread-0 格式的名称

  6. 线程id是全局递增的,从 1 开始

4 线程启动

看一下 start() 方法的源码:

public synchronized void start() {
    // 状态不为 0(对应着 NEW 状态),则抛出异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
  	// 将线程加入线程组
    group.add(this);
    boolean started = false;
    try {
        start0();
      	// 启动成功,标记为启动成功
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}

4.1 多次调用 start()

永远都不能对一个线程多次调用和执行start()方法:

if (threadStatus != 0)
        throw new IllegalThreadStateException();

线程一旦执行过一次以后,那么它的 threadStatus 就一定会变为非 0 的一个状态。如果是非0状态,那么再次调用 start() 方法,会抛出一个异常IllegalThreadStateException(非法的线程状态的异常)。

4.2 启动成功

一旦是start0()成功地启动之后,他就会去执行我们重写的run()方法(如果传入进去的是 Runnalbe 对象,就会执行那个Runnable对象的方法)。

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

如果采用如下方式:

new Thread(new Runnable() {
	public void run() {
	}
}).start();

传递一个 Runnable 对象(target),如果 target 不为 null 的话,那么此时就会执行 target 的 run方法。反之,如果你是直接自己用Thread 类继承了一个子类的话,那么你会重写这个 run()方法,start0()启动线程之后,就会来执行你的run()方法。

5 线程启动总结

  1. 一旦启动了线程之后,就不能再重新启动了。再次调用start()方法,会抛出异常。因为启动之后,threadStatus就会变成非0的状态。

  2. 启动线程之后,这个线程就会加入之前处理好的那个线程组中。

  3. 启动一个线程实际上走的是 native 方法,start0(),会实际的启动一个线程

  4. 一个线程启动之后就会执行run()方法。