创建线程的三种方式-继承Thread,实现Runnable以及实现Callable

一. 创建线程的三种方式

1. 实现 Runnable接口

public class RunnableExample implements Runnable {
    @Override
    public void run() {
        System.out.println("implement Runnable");
    }

    public static void main(String[] args) {
        RunnableExample runnableExample = new RunnableExample();
        new Thread(runnableExample).start();
        //lambda 简写
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + ": run");
        }, "runnable-example").start();
    }
}

2. 继承 Thread类 Thread类实现类Runnable接口

public class ThreadExample extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": extend Thread");
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadExample threadExample = new ThreadExample();
        Thread thread = new Thread(threadExample);
        thread.start();
   }
 }  

3.实现Callable 接口

public class CallableExample implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "oh, my god ";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
        CallableExample callableExample = new CallableExample();
        FutureTask<String> result = new FutureTask<String>(callableExample);
        new Thread(result).start();
        String res = result.get();
        System.out.println(res);
    }
}

二. 源码分析三种方式

1. Runnable接口

Runnable接口,其实就是提供一个run()方法,让实现它的类实现这个run()方法。

@FunctionalInterface
public interface Runnable {
    /**
     * 当使用实现接口Runnable的对象创建线程时,启动该线程将导致在该单独执行的线程中调用对象的run方法。 
     * run方法的一般约定是,它可以采取任何操作
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

2. Callable接口

Callable泛型接口,其实就是提供一个有返回类型的call()方法,让实现它的类实现这个call()方法。

/**
 * 返回结果并可能引发异常的任务。实现者定义了一个没有参数的方法call。
 *
 * Callable接口与Runnable类似,因为它们都是为实例可能由另一个线程执行的类设计的。
 * 但是,Runnable不返回结果,也不能抛出检查过的异常。
 * executor类包含实用程序方法,用于从其他常见表单转换为可调用类。
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * 计算结果,或在无法这样做时抛出异常
     *
     * @return 计算结果
     * @throws Exception 无法这样做时抛出异常
     */
    V call() throws Exception;
}

3. Thread类

Thread实现类Runnable接口,所以实现了run()方法。

public class Thread implements Runnable {
    /* 确保本地注册(类构造器方法方法用于类初始化)是创建一个线程首要做的事情。
     * 注册的都是一些本地方法。
     */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name; //线程名称,可更改且线程可见
    private int            priority;//线程优先级
    private Thread         threadQ;  //没发现用到
    private long           eetop;   //没发现用到

    /* 线程是否单步 */
    private boolean     single_step; 

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

    /* JVM state */
    private boolean     stillborn = false;

    /* 从构造方法传过来的Runnable */
    private Runnable target; 

    /* 此线程所属的组别 */
    private ThreadGroup group;

    /* 此类型的类加载器 */
    private ClassLoader contextClassLoader;

    /* 此线程继承的访问控制上下文*/
    private AccessControlContext inheritedAccessControlContext;

    /* 用于自动编号的匿名线程 默认为0 */
    private static int threadInitNumber;
    //得到下一个线程编号从0开始 
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /* 与此线程相关的ThreadLocal值。此映射由ThreadLocal类维护。 */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * 与此线程相关的可继承线程本地值。此映射由InheritableThreadLocal类维护。
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * 此线程请求的堆栈大小,如果创建者未指定堆栈大小,则为0。
     * 这取决于VM对这个数字做它喜欢做的任何事情;一些VM会忽略它。
     */
    private long stackSize;

    /*
     *  此变量表示:在本地线程终止后,JVM私有的一个状态值   
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID 为1开始
     */
    private long tid;

    /* 用于生成线程ID*/
    private static long threadSeqNumber;

    /* 
     * 为工具提供的线程状态值,初始化值表示当前线程还未运行
     */
    //初始状态
    private volatile int threadStatus = 0;

     //私有同步方法,获取下一个线程id 从1开始
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }
    
      /**
      * 此变量为用于调用java.util.concurrent.locks.LockSupport.park方法的参数.
      * 其值由方法(private) java.util.concurrent.locks.LockSupport.setBlocker进行设定.
      * 其值访问由方法java.util.concurrent.locks.LockSupport.getBlocker进行获取.
      */
    volatile Object parkBlocker;

    /**
      * 在可中断I/O操作中,本线程中的此对象会被阻塞.
        如果此线程的中断状态位被设置,则应该调用此阻塞对象的中断方法.
      */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    /*设定block变量的值;通过java.nio代码中的 sun.misc.SharedSecrets进行调用.
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    /**
     * 一个线程可以拥有的最低优先级 1
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * 线程的默认优先级 5 
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * 一个线程可以拥有的最高优先级. 10
     */
    public final static int MAX_PRIORITY = 10;

    /**
     * 返回当前正在执行线程对象的引用,注意这是一个本地方法。
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();

    /**
     *  yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。
     * 因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。
     * 但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
     */
    public static native void yield();

    /**
     * 此方法会引起当前执行线程sleep(临时停止执行)指定毫秒数.
     * 此方法的调用不会引起当前线程放弃任何监听器(monitor)的所有权(ownership).
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * 同上 如果nanos >= 50万 millis+1 如果小于50万 millis不变
     */
    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);
    }

    /**
     * 利用当前访问控制上下文(AccessControlContext)来初始化一个线程.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 初始化一个线程
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize  值为0表示此参数被忽略
     * @param acc 用于继承的访问控制上下文
     * @param inheritThreadLocals 如果值为true,从构造线程继承可继承线程局部变量的初始值
     */
    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();
        //如果所属线程组为null
        if (g == null) {
            //检测其是否为一个应用
            //如果有安全管理,查询安全管理需要做的工作
            if (security != null) {
                g = security.getThreadGroup();
            }

            //如果安全管理在线程所属线程组的问题上没有什么强制的要求strong opinion of the matter
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

      //无论所属线程组是否显示传入,都要进行检查访问.
        g.checkAccess();

        //检查是否有required权限 enableContextClassLoaderOverride
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        //当前正在执行的线程是否为守护线程.
        this.daemon = parent.isDaemon();
        //优先级
        this.priority = parent.getPriority();
        //证明创建当前子类实例能够忽略安全限制:子类不能覆盖安全敏感,非final类型的方法,
        //否则enableContextClassLoaderOverride这个运行时许可会被坚持.
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
      	//目标runnable 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;

        /* 设置线程ID,默认从1开始(++0) */
        tid = nextThreadID();
    }

    /**
     * 线程不支持浅拷贝.取而代之的是构造一个新的线程.
     *
     * @throws  CloneNotSupportedException
     *          always
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * 分配一个新的线程对象.此构造器和Thread(ThreadGroup,Runnable,String) 构造器的效果一样.
     * 
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
  
    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);
    }

    /**
     *  此方法的调用会引起当前线程的执行;JVM会调用此线程的run()方法.
     * 结果就是两个线程可以并发执行:当前线程(从调用的start方法返回)和另一个线程(它在执行run方法).
     * 一个线程可以被调用多次.
     * 尤其注意:一个线程执行完成后可能并不会再被重新执行. 
     */
    public synchronized void start() {
          /**
         * 此方法并不会被主要方法线程or由虚拟机创建的系统组线程所调用.
         * 任何向此方法添加的新功能方法在未来都会被添加到虚拟机中.
         * 0状态值代表了NEW的状态. 此处表示不能重复start,会报错。
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* 通知线程组组此线程即将启动,以便可以将其添加到组的线程列表中,并且可以减少组的未启动计数。 */
        group.add(this);
        //默认还没开启
        boolean started = false;
        try {
        //调用本地start0方法,设置当先线程已启动
            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 */
            }
        }
    }

    private native void start0();

    /**
     * 如果此线程有runable对象,则执行,否则什么也不执行.
     *
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    /**
     * 此方法由系统调用,用于在一个线程退出前做一些扫尾工作.
     */
    private void exit() {
        //如果线程组不为空
        if (group != null) {
            // 从group中  移除线程
            group.threadTerminated(this);
            group = null;
        }
        /*
          这样一来, 在线程消亡后, 就没有任何对象会持有threadlocalMap 对象的引用了, 自然里面存储的对象就可以被JVM 回收了。
          */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

 /**
  * 强制线程退出.此时会创建一个新的对象ThreadDeath作为异常.
  * 允许对一个还没有start的线程执行此方法.如果当前线程已经start了,则此方法的调用会使其立即停止.
  * 客户端不应该经常去捕获ThreadDeath异常,除非有一些额外的清除工作要做(注意:在线程死亡前,ThreadDeath的异常在抛出
  * 时会引发try对应的finally代码块的执行).如果catch捕获了ThreadDeath对象,必须重新抛出此异常以保证线程可以真正的死亡.
  * 最顶级的错误处理器会对其它未被捕获类型的异常进行处理,但是如果未处理异常是线程死亡的实例,则不会打印消息或通知应用程序。
    *此方法已经废弃不用了 
  */
    @Deprecated
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }
        // A zero status value corresponds to "NEW", it can't change to
        // not-NEW because we hold the lock.
        if (threadStatus != 0) {
            resume(); //如果线程是suspended挂起的,还有个唤醒操作,有可能会发生死锁
        }

        // 虚拟机能处理所有的线程
        stop0(new ThreadDeath());
    }

    /**
     *
     */
    @Deprecated
    public final synchronized void stop(Throwable obj) {
        throw new UnsupportedOperationException();
    }

    /**
     *  此方法功能:中断当前线程.
         * 除非当前线程在中断自己(这么做是允许的),此线程的checkAccess()方法被调用且抛出异常SecurityException
         * 1.如果当前线程由于wait类型方法,join类型方法或者sleep类型的方法的调用被阻塞,则它的中断状态将被清除且会收到一个
         * 中断异常InterruptedException
         * 2.如果此线程由于java.nio.channels.InterruptibleChannel类中的InterruptibleChannel的I/O操作而被阻塞,
         * 则此方法会导致通道被关闭,且线程的中断状态会被重置,同时线程会收到一个异常ClosedByInterruptException.
         * 3.如果此线程由于java.nio.channels.Selector而阻塞,则线程的中断状态会被重置,且它将立即从阻塞的selection操作返回,
         * 且返回值通常是一个非零值,这就和java.nio.channels.Selector#wakeup的wakeup()方法被调用一样.
         * 4.如果前面的条件都不成立,那么该线程的中断状态将被重置.。
         * 中断一个处于非活着状态的线程并不需要产生任何其它影响.
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();// 只是为了设定中断标识位
                b.interrupt(this); //中断当前线程
                return;
            }
        }
        interrupt0();//只是为了设置中断标识位
    }

    /**
     * 测试当前线程是否被中断.
     * 线程的中断状态会被此方法清除.
     * 换句话说,如果此方法两次调用都能成功,则第二次调用的返回结果为false(除非在第一次调用完后和第二次调用前,当前线程被再次中断)
     * 因为在中断方法被调用时线程并未处于alive状态而忽略线程中断的情况会由于此方法的调用而受到影响.
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
      * 查看当前线程是否被中断.
     * 此方法的调用不会影响当前线程的中断状态.
     * 因为在中断方法被调用时线程并未处于alive状态而忽略线程中断的情况会由于此方法的调用而受到影响.
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * 测试一些线程是否被中断.
     * 中断状态会被重置or并不依赖于之前的中断清除的值.
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }

    //测试当前线程是否处于存活状态.如果一个线程在死亡状态前都是存活状态.
    public final native boolean isAlive();

    //将一个线程挂起
    @Deprecated
    public final void suspend() {
        checkAccess();
        suspend0();
    }

    //恢复一个悬挂状态的线程
    @Deprecated
    public final void resume() {
        checkAccess();
        resume0();
    }

    /**
     *  配置优先级
     */
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    public final int getPriority() {
        return priority;
    }

    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }


    public final String getName() {
        return name;
    }
    
    public final ThreadGroup getThreadGroup() {
        return group;
    }

    /**
     * //返回线程所属组的线程数,这是一个估计值.
     */
    public static int activeCount() {
        return currentThread().getThreadGroup().activeCount();
    }

   /**
     * 将线程所属组和其子组中所有活着的线程拷贝到参数数组中.
     * 此方法只调用了一个方法java.lang.ThreadGroup.enumerate().
     * 一个应用如果想获得这个线程数组,则它必须调用此方法,然而如果此数组太小而无法存放所有的线程,则放不下的线程
     * 就自动被忽略了.其实从线程组里面获取存活线程的方法是受到争议的,此方法调用者应该证明方法返回值应该严格小于
     * 参数数组的长度.
     * 因为此方法在被调用时存在竞争,因此建议此方法只用于debug和监听目的.
     */
    public static int enumerate(Thread tarray[]) {
        return currentThread().getThreadGroup().enumerate(tarray);
    }

    /**
     * Counts the number of stack frames in this thread. The thread must
     * be suspended.
     *
     * @return     the number of stack frames in this thread.
     * @exception  IllegalThreadStateException  if this thread is not
     *             suspended.
     * @deprecated The definition of this call depends on {@link #suspend},
     *             which is deprecated.  Further, the results of this call
     *             were never well-defined.
     */
    @Deprecated
    public native int countStackFrames();

    /**
     * 最多等待参数millis(ms)时长当前线程就会死亡.参数为0时则要持续等待.
       * 此方法在实现上:循环调用以this.isAlive()方法为条件的wait()方法.
       * 当线程终止时notifyAll()方法会被调用.
       * 建议应用程序不要在线程实例上使用wait,notify,notifyAll方法.
     */
    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导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者等待指定的时间。
              	//jvm 在调用join,线程执行完的时候,会自动调用notitfy_all
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay); //Object的方法
                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);
    }

    /**
     * Prints a stack trace of the current thread to the standard error stream.
     * This method is used only for debugging.
     *
     * @see     Throwable#printStackTrace()
     */
    public static void dumpStack() {
        new Exception("Stack trace").printStackTrace();
    }

    /**
     * 设置是否为守护线程
     */
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    /**
     * Tests if this thread is a daemon thread.
     *
     * @return  <code>true</code> if this thread is a daemon thread;
     *          <code>false</code> otherwise.
     * @see     #setDaemon(boolean)
     */
    public final boolean isDaemon() {
        return daemon;
    }

     
    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }

    public String toString() {
        ThreadGroup group = getThreadGroup();
        if (group != null) {
            return "Thread[" + getName() + "," + getPriority() + "," +
                           group.getName() + "]";
        } else {
            return "Thread[" + getName() + "," + getPriority() + "," +
                            "" + "]";
        }
    }

    /**
		 *  获取当前线程的类加载器
     * @since 1.2
     */
    @CallerSensitive
    public ClassLoader getContextClassLoader() {
        if (contextClassLoader == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(contextClassLoader,
                                                   Reflection.getCallerClass());
        }
        return contextClassLoader;
    }

    /**
 	   * 设置上下文类加载器
     */
    public void setContextClassLoader(ClassLoader cl) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }
} 

4. 重点关注方法

Thread.join()方法

 /**
     *  millis 等待线程死亡的时间最多为millis毫秒。超时为0意味着永远等待。
     *  这个实现使用这个的循环。在此条件下等待呼叫,我还活着。当线程终止此时。
     * 调用notifyAll方法。建议应用程序不要在线程实例上使用wait、notify或notifyAll。     
     * @param  millis
     *         等待线程死亡的时间最多为millis毫秒。超时为0意味着永远等待。
     *
     * @throws  IllegalArgumentException
     *          millis 为负数 抛出异常
     *
     * @throws  InterruptedException
     *          如果有线程中断了当前线程。抛出此异常时清除当前线程的中断状态
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
		//millis 为负数 抛出异常
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
		//millis为0 无限等待
        if (millis == 0) {
        	//测试此线程是否处于活动状态。如果一个线程已经启动并且还没有死,那么它就是活的
            while (isAlive()) {
            //导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者等待指定的时间。
                wait(0);
            }
        } else {
           //wait millis 时间
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

wait() 没有被唤醒,是怎么执行下面的流程?cpp里面

// 位于/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
    // ...

    // Notify waiters on thread object. This has to be done after exit() is called
    // on the thread (if the thread is the last thread in a daemon ThreadGroup the
    // group should have the destroyed bit set before waiters are notified).
    // 有一个贼不起眼的一行代码,就是这行
    ensure_join(this);

    // ...
}

static void ensure_join(JavaThread* thread) {
    // We do not need to grap the Threads_lock, since we are operating on ourself.
    Handle threadObj(thread, thread->threadObj());
    assert(threadObj.not_null(), "java thread object must exist");
    ObjectLocker lock(threadObj, thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
    java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    // Clear the native thread instance - this makes isAlive return false and allows the join()
    // to complete once we've done the notify_all below
    java_lang_Thread::set_thread(threadObj(), NULL);

    // thread就是当前线程,被唤醒
    lock.notify_all(thread);

    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
}

Thread.interrupt()方法

在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出。

Thread类定义了如下方法:

public boolean isInterrupted();//测试此线程是否已被中断。此方法不影响线程的中断状态
public void interrupt();//中断线程
public static boolean interrupted();//测试此线程是否已被中断,并清空中断标志位
interrupt()对线程的影响与线程的状态和在进行的IO操作有关,我们先考虑线程的状态:

RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度

WAITING/TIMED_WAITING:线程在等待某个条件或超时

BLOCKED:线程在等待锁,试图进入同步块

NEW / TERMINATED:线程还未启动或已结束

/**
 * 线程中断总结
 * Thread  用来验证,线程处于WAITING/TIMED_WAITING 时,调用interrupt()抛出中断异常,并且中断标志恢复为false
 * Thread2 用来验证:如果线程在RUNNABLE时,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,中断标志为true
 * Thread3 和 Thread4 用来验证:)线程处于BLOCKED状态,interrupt()只是会设置线程的中断标志位,也就是说,interrupt()并不能使一个在等待锁的线程真正"中断"。
 * @author 
 * @date 2020/06/11
 */
public class InterruptedExample {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("enmmm");
                    //thread 线程处于wait状态
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println("---InterruptedException---");
                    //false
                    System.out.println(isInterrupted());
                    e.printStackTrace();
                }
                System.out.println("a");
            }
        };
        thread.start();
        //处于wait的状态会interrupt()中断异常
        thread.interrupt();
        

        Thread thread2 = new Thread() {
            int i = 0;

            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("没有中断执行中" + ++i);
                }
                System.out.println(Thread.currentThread().isInterrupted());
                System.out.println("中断了");
            }
        };
        thread2.start();
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName());

        } catch (InterruptedException e) {
            System.out.println("thread2 中断异常");
            e.printStackTrace();
        }
        thread2.interrupt();


        Object o = new Object();

        Thread thread3 = new Thread() {
            @Override
            public void run() {
                try {
                    synchronized (o) {
                        System.out.println("enmmm");
                        //thread 线程处于wait状态
                        Thread.sleep(3000);
                        System.out.println("enmm3000m");
                    }
                } catch (InterruptedException e) {
                    System.out.println("---InterruptedException---");
                    //false
                    System.out.println(isInterrupted());
                    e.printStackTrace();
                }
                System.out.println("a");
            }
        };


        //如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正"中断"。
        //使用synchronized关键字获取锁的过程中不响应中断请求,这是synchronized的局限性。如果这对程序是一个问题,应该使用显式锁。
        Thread thread4 = new Thread() {
            @Override
            public void run() {
                synchronized (o) {

                    System.out.println("thread4");
                }
            }
        };

        thread3.start();
        try {
            Thread.sleep(1000);
            thread4.start();
            thread4.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

线程中断总结

  • 线程处于WAITING/TIMED_WAITING 时,调用interrupt()抛出中断异常,并且中断标志恢复为false
  • 线程处于RUNNABLE时,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,中断标志为true
  • 线程处于BLOCKED状态,interrupt()只是会设置线程的中断标志位,也就是说,interrupt()并不能使一个在等待锁的线程真正"中断"。

5. 从源码中,可以总结几点平时没有留意到的

  • 线程初始化的时候,默认名称为Thread-N(N从0开始),线程ID 默认从1开始。
  • 线程不能重复调用start()方法,因为每次判断线程的状态是否为NEW状态,不为NEW抛异常。
  • 线程使用join()方法,可以保证线程的顺序执行完毕,里面用到了wait()方法,等待唤醒条件。
  • 线程interrupted()方法 ,不一定能使线程真的中断。

三. 线程的生命周期

1. 线程的几种状态

生命周期的六种状态

2. 线程状态之间的转换

六种状态的转换

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章