线程的两种创建方式、启动源码解析

创建线程的两种方式

1.在Thread子类覆盖的run方法中编写运行代码

Thread thread1 = new Thread() {
    public void run() {
        while (true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("1 : " + Thread.currentThread().getName());
            System.out.println("2 : " + this.getName());
        }
    }
};
thread1.start();

2.在传递给Thread对象的Runnable对象的run方法中编写代码

Thread thread2 = new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("1' : " + Thread.currentThread().getName());
            // 这里为什么不能使用 this.getName();??
            // 首先,这里如果使用 this 的话,代表的是一个 Runnable 接口的实现对象
            // 而且上述只是实现了其run方法,并没有任何与getName方法有关的定义,如果直接调用自然会报错
            // System.out.println("2' : " + this.getName());
        }
    }
     // 加入再下面扩展了getName 方法的话,那么就可以简单的实现可以调用了
    public String getName() {
        return Thread.currentThread.getName();
    }
});
thread2.start();

总结:总结:查看Thread类的run方法的源码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。

接下来我们分析一下源码:

1.首先在Thread的源码中,run方法的定义如下:

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

在上面的源码中 target 为Thread类中的一个 Runnable 类型的局部变量,假如我们在创建一个线程的时候传入了我们重写过的 Runnable ,那么我们就会执行 Runnable 接口中的run方法的内容。

源码中的有关 Runnable 类型的 target 定义如下:

    /* What will be run. */
    private Runnable target;

在源码中创建线程时,传入Runnable类型的构造方法如下:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

在该构造方法中是调用了一个init方法,这里调用的init方法,是在Thread类中的一个重载的方法,这里调用的init方法的内部实现是调用了另一个init方法,而另一个init方法才是Thread类真正的init实现方法。上面调用的init方法源码:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {    
           /* 在配置的时候传入了 
            该线程所属的线程组、线程的宿主对象、线程的名字、
            还有线程请求的堆栈大小(每个线程都有自己的堆栈,如果这里不设置默认为0)
            */
        init(g, target, name, stackSize, null, true);
    }

上面init方法中调用的init方法:

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,    
                  //在这个方法中增加了一个 AccessControlContext类,
                  //该类用于根据封装的上下文进行系统资源访问决策
                  boolean inheritThreadLocals) {    
                  // 这是一个是否继承ThreadLocals的标志位
    if (name == null) {
        throw new NullPointerException("name cannot be null");    
        // 线程的名字是必须要有的,上面的init方法已经默认帮我们设置好了名字
    }

    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();
    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();    // 设置线程ID
}

线程的创建解决了,我们再看看线程启动的时候发生了啥?

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".
         */
        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;    // 设置启动标志位为false
        try {
            start0();    // 调用native方法,底层启动线程
            started = true;    // 设置启动标志位为 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 */
            }
        }
    }
// 线程启动的Native方法
private native void start0();

问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?

从上面的源码中我们也可以看到,Thread类的run方法是默认去判断有没有 Runnable 类的传入,但是假如子类重写了run方法之后,那么就没有了判断是否存在 Runable 类的这一个步骤,所以会执行子类的run方法的代码。

你可能感兴趣的:(并发编程)