我的jdk源码(六):Thread类

一、概述

    Tread类是线程基础类。 线程是系统资源分配的最小单位,它被包含在进程之中,是进程中的实际运作单位。在实际的开发场景中,总会遇到多线程的问题,那么学好Thread类的源码能够有助于我们有更加清晰的认识。话不多说,直接进入源码! 

二、源码分析

    (1)类的声明,源码如下:

//一个“线程”是指在程序中执行的线程。Java虚拟机允许应用多个线程并发运行。
//每个线程都有优先级,优先级高的先执行。线程可能是守护线程或者不是。
//线程的优先级等于创建线程的优先级,当且仅当一个线程是守护线程,创建出来的线程才是守护线程。
//通常JVM启动,有一个非守护线程作为主线程。只有当Runtime.exit被调用或者所有非守护线程死亡时(run执行完毕并返回/抛出异常)JVM会停止运行这些线程。
//两种创建线程的方法:继承Thread类/实现Runnable接口
//每个线程有自己的名称用来标识自己。但可能多个线程会重名,如果启动时没有创建名字,会自动生成一个。
public class Thread implements Runnable

    我们都知道,创建一个线程有两种方式,继承Thread类和实现Runnable接口,由于Thread本身就实现了Runnable接口,所以也解释了继承Thread也是一样的效果。两种方式创建的线程启动方式不一样,继承Thread类的线程,实例化对象thread以后,可以直接调用自身的start()方法,即thread.start();实现Runnable接口的线程,实例化以后,需要使用new Thread(thread).start()来启动。Runnable接口里只有一个抽象方法run(),子类不需要运行此方法,当线程start()方法调用后,线程进入准备就绪状态,当获取到了CPU资源的时候,就自动执行run()方法。

    (2)静态方法与静态代码块,源码如下:

    private static native void registerNatives();
    static {
        registerNatives();
    }

    此处的写法与Object类中的写法一致,具体意义以及作用请参看《我的jdk源码(一):Object 一切类的根本!》。

    (3)成员变量,源码代码如下:

    //线程名称
    private volatile String name;
    //线程优先级,最低1,最高10,默认5
    private int            priority;
    //
    private Thread         threadQ;
    //
    private long           eetop;
    //是否单步执行线程
    private boolean     single_step;

    //是否为守护线程
    private boolean     daemon = false;

    //JVM状态
    private boolean     stillborn = false;

    //run方法执行的目标代码
    private Runnable target;

    //线程所属的线程组
    private ThreadGroup group;

    //线程类加载器
    private ClassLoader contextClassLoader;

    //
    private AccessControlContext inheritedAccessControlContext;

    //生成默认线程名的计数器
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    //Thread类中,有两个ThreadLocal.ThreadLocalMap成员变量,能保证在操作ThreadLocalMap的时候,每个线程实例都有自己的ThreadLocalMap来操作数据,保证线程之间的数据隔离
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    //线程请求栈的深度
    private long stackSize;

    //本地线程终止后JVM私有值
    private long nativeParkEventPointer;

    //线程id
    private long tid;

    //用于生成id
    private static long threadSeqNumber;

    //线程状态值,0表示未运行
    private volatile int threadStatus = 0;

    //获取下一个线程
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    //parkBlocker用于调用java.util.concurrent.locks.LockSupport.park方法
    //通过java.util.concurrent.locks.LockSupport.setBlocker方法set
    //通过java.util.concurrent.locks.LockSupport.getBlocker方法get
    volatile Object parkBlocker;

    //
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();
    //线程最低优先级
    public final static int MIN_PRIORITY = 1;
    //线程默认优先级
    public final static int NORM_PRIORITY = 5;
    //线程最高优先级
    public final static int MAX_PRIORITY = 10;

    (3)线程状态,源码如下:

    //任一时刻线程只能处于其中的一个状态,且只是虚拟机的状态值,并不会反映操作系统的线程状态
    public enum State {
        //尚未启动状态,线程没有调用start方法之前的状态
        NEW,

        //可运行状态,线程在JVM里面运行的状态,包括就绪和运行
        RUNNABLE,

        //阻塞状态,线程等待监视器锁的状态
        BLOCKED,

        //等待状态,一个线程等待其他线程的状态,这种等待是无限期的
        WAITING,

        //定时等待状态,一个线程等待其他线程的状态,这种等待是有限期的
        TIMED_WAITING,

        //终止状态,线程执行完毕已经退出的状态
        TERMINATED;
    }

    (4) 构造函数,源码如下:

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

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

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

    public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

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

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }

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

    我们可以看到,所有的构造方法都是调用的init()方法,我们再来看看init()的源码,如下:

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        //名字不可以为null,否则抛出异常
        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();
            }
        }
                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;

        //该方法是一个id自增功
        tid = nextThreadID();
    }

    (5) 重要成员方法之start()方法,源码如下:

    public synchronized void start() {
        //threadStatus =0表示状态为new还没有调用过start()方法
        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) {
                
            }
        }
    }
    //本地方法快速启动线程
    private native void start0();

    通过源码分析可以看出,start()方法在调用时,会先检查是否曾经已经调用过,如果有则抛出异常。然后先将当前启动的线程加入到当前的线程组group中,然后调用本地方法start0()方法,最后判断如果启动失败,调用线程组group的threadStartFailed()方法移除刚加入的线程并释放资源。 此时线程会被放置到等待队列中且状态由“新建”进入“就绪”,等待调度器分配资源,随时进入到“运行”状态。 threadStartFailed()方法源码如下:

    void threadStartFailed(Thread t) {
        synchronized(this) {
            remove(t);
            nUnstartedThreads++;
        }
    }

    (6) run()方法源码如下:

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

    Thread中的run()方法实际上只是实现了Runnable接口的run()方法,直接调用是无效的,它不会产生任何实际结果 。run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。 

    (7) interrupt()方法与interrupted()方法和isInterrupted()方法源码如下:

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    //返回中断状态并且清除中断状态
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    //返回中断状态,但不会清除中断状态
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
    //本地方法,返回中断状态,参数ClearInterrupted设置是否清除中断状态
    private native boolean isInterrupted(boolean ClearInterrupted);
    //本地方法,打断当前线程
    private native void interrupt0();

    Thread的interrupt()方法相比已经弃用的stop()方法,能真正实现线程中断。interrupted()方法和isInterrupted()方法都可以获取中断标记,返回值为boolean类型,interrupted()会返回中断状态并且清除此状态。interrupt方法调用会中断线程,线程状态会被置为”中断”标志,但是中断线程并不会造成停止线程的执行,只是仅仅会把线程状态置为”中断”。当线程在执行阻塞操作时(比如调用sleep/wait/yield/join方法时)调用了interrupt()会抛出InterruptException异常并且将该线程的”中断”标志位清空,会将线程的中断标志重新设置为false。

    (8) yield()方法,源码如下:

    public static native void yield();

    Thread本地yield()方法作用于建议CPU暂停执行当前线程,让CPU执行其他同级线程。使用时需要搭配线程优先级来使用, 它的实际运行流程是先检测当前是否有相同优先级的线程处于同可运行状态,如果有则把CPU的占有权交给此线程,否则继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程。

    (9) wait()方法,源码如下:

    public final native void wait(long timeout) throws InterruptedException;
    
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }

    使调用wait()方法的线程释放共享资源锁,然后从运行状态退出,进入等待队列,直到被再次唤醒 或 定时等待 N 毫秒,如果没有通知就超时返回。wait()方法属于Object的方法, 使用时首先需要获得锁,一般放在同步方法或同步代码块中,由synchronized关键字修饰,需要重新唤醒时由notify()方法和notifyAll()方法来唤醒。

    (10) join()方法,源码如下:

    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    public final synchronized void join(long millis, int nanos)
    throws InterruptedException {

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }

    public final void join() throws InterruptedException {
        join(0);
    }

     join()方法的作用就是将调用join的线程优先执行,当前正在执行的线程阻塞,直到调用join方法的线程执行完毕或者被打断,主要用于线程之间的交互。

    (11) sleep()方法,源码如下:

    public static native void sleep(long millis) throws InterruptedException;

    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

    此方法会使当前线程在指定的时间内暂停执行, 暂停过程中如果调用此线程对象的interrupt()方法则会唤醒线程并且抛出InterruptedException异常,并继续执行线程。

    (12) notify()方法和notifyAll()方法,源码如下:

    public final native void notify();

    public final native void notifyAll();

    这两个方法不属于Thread类中的方法,而是Object类中的方法,但是这两个方法只有在线程中才有使用场景,作用在于唤醒正在等待状态的线程。notify()方法随机唤醒等待队列中等待同一共享资源的线程,此线程回退出等待队列,进入可运行状态。notifyAll()方法则是唤醒所有正在等待队列中等待同一共享资源的全部线程,全部退出等待队列,进入可运行状态,优先级最高的开始执行。

    (13) currentThread()方法源码如下:

    public static native Thread currentThread();

    此方法为本地方法,返回当前正在执行的线程对象的引用。

    此外,其他那些获取属性的方法就不一一介绍了。以上方法主要涉及到线程整个生命周期中状态发生变化时所涉及到的方法,如下图所示:

三、总结

    本篇文章着重讲解了Thread的源码内需要重点关注的方法等,没有详细讲解线程以及并发编程的一些问题,后面会专门出专题讲解。接下来敬请期待《我的jdk源码(七): ThreadLocal》。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

猜你喜欢

转载自blog.csdn.net/qq_34942272/article/details/106373608