🟤 Thread

吞佛童子2022年6月20日
  • Java
  • concurrency
大约 10 分钟

🟤 Thread

1. 类注释

  • JVM 允许多线程并发执行

  • 每个线程都有用一个优先级:优先级越高说明线程越容易被执行
  • 每个线程可以被设置为 守护线程
  • 若某个线程里面又创建了一个新的线程,那么这个新线程和父线程有相同的优先级,且若父线程为 守护线程,则该新线程也为守护线程

  • 当 JVM 运行时,通常有一个 main 的非守护线程
  • JVM 会继续运行,直到有以下某种情况发生:
    • 在 Runtime 期间执行 exit()
    • 所有 非守护线程 均已执行完毕,要么执行 run() 后正常返回,要么在 run() 中抛出异常

  • 创建新线程的 2 种方式:
    • 创建一个类 extends Thread,并重写里面的 run() 方法
    • 创建一个类 implements Runnable,并重写里面的 run() 方法

  • 每个线程都有一个名字用于区分,也许有多个线程拥有同一个名字
  • 如果一个线程在创建的时候没有指定名称,则会被自动分配一个名称

2. 类图

public class Thread implements Runnable {
    // ...
}

img_3.png


3. 属性

    /* Make sure registerNatives is the first thing <clinit> does. */
    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; // 是否为 守护线程

    private boolean     stillborn = false; // JVM 当前状态

    private Runnable target; // 执行的是 target 的 run() 方法

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

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

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    private static int threadInitNumber; // 用于线程的命名
    
    ThreadLocal.ThreadLocalMap threadLocals = null; // ThreadLocal 相关
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
    private long stackSize; // 栈深度,如果创建时没有指定,则为 0;有的 JVM 用到了这个参数,有的没有用到
    
    private long nativeParkEventPointer; // 本机线程终止后持续存在的 JVM 私有状态
    
    private long tid; // 线程 id
    
    private static long threadSeqNumber; // 用于生成线程 id
    private volatile int threadStatus = 0; // 线程状态
    
    volatile Object parkBlocker; // LockSupport.park 时有用
    
    private volatile Interruptible blocker; // 用于 线程的 interruptible I/O
    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; // 最高优先级

4. 构造函数

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

    /**
     * @param  target - 当当前线程执行 start() 时,执行的是 target 所在类的 run() 方法
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 创建一个带指定权限控制的线程 - 为非 public 构造器
     */
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }

    /**
     * @param  group - 线程所在的组
     *
     * @param  target - 线程启动时,执行的 run() 方法所在的类
     *
     * @throws  SecurityException - 在指定线程组下无法创建线程
     */
    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * @param   name - 指定线程名称
     */
    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);
    }
    
    /**
     * 线程的初始化
     * @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 - 线程所在的组
     * @param target - 执行 target 对象的 run() 方法
     * @param name 线程的名称
     * @param stackSize - 线程想要的栈深度
     * @param acc - 想要的 权限控制
     * @param inheritThreadLocals - 若为真,则需要继承
     */
    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) {
            /* 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);
        this.stackSize = stackSize;

        tid = nextThreadID();
    }
    
    /**
     * 获取当前正在执行的线程对象
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();
    
    /**
     * 判断是否有修改当前线程的权限
     * <p>
     * If there is a security manager, its <code>checkAccess</code> method
     * is called with this thread as its argument. This may result in
     * throwing a <code>SecurityException</code>.
     *
     * @exception  SecurityException  if the current thread is not allowed to
     *               access this thread.
     * @see        SecurityManager#checkAccess(Thread)
     */
    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }
    
    /**
     * Verifies that this (possibly subclass) instance can be constructed
     * without violating security constraints: the subclass must not override
     * security-sensitive non-final methods, or else the
     * "enableContextClassLoaderOverride" RuntimePermission is checked.
     */
    private static boolean isCCLOverridden(Class<?> cl) {
        if (cl == Thread.class)
            return false;

        processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
        WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
        Boolean result = Caches.subclassAudits.get(key);
        if (result == null) {
            result = Boolean.valueOf(auditSubclass(cl));
            Caches.subclassAudits.putIfAbsent(key, result);
        }

        return result.booleanValue();
    }
    
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

5. 内部类

    /**
     * 线程状态,一个线程只能处于以下的其中一种状态:
     */
    public enum State {
        /**
         * 还没有 start 的线程
         */
        NEW,

        /**
         * 运行中的线程
         * 被 JVM 执行,但也许需要等待 操作系统 分配资源
         */
        RUNNABLE,

        /**
         * 阻塞状态 - 等待获取一个监视器锁
         */
        BLOCKED,

        /**
         * 等待状态
         * 可能由于执行了以下方法:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         */
        WAITING,

        /**
         * 有限时间等待状态
         * 可能执行了以下方法:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * 终止状态
         */
        TERMINATED;
    }

6. 常见方法

1) currentThread()

    /**
     * 返回当前正在执行的线程对象
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();

2) yield()

     /**
     * 告知 调度程序 当前线程愿意放弃对 当前 CPU 的使用,调度程序也可以忽略此提示
     *
     * 常用于 测试 | 并发包的并发控制
     */
    public static native void yield();

3) sleep()

    /**
     * 睡眠
     * 期间不参与 系统调度
     * 不会释放已经拥有的 资源
     *
     * @param  millis - 毫秒
     *
     * @throws  IllegalArgumentException - 输入毫秒数 < 0
     *
     * @throws  InterruptedException - 其他线程中断了当前线程;当异常被抛出时,interrupted 状态被清空
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * @param  millis - 毫秒
     * @param  nanos - 纳秒数 ∈ [0, 999999]
     *
     * @throws  IllegalArgumentException
     * @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);
    }

Thread.yield () VS Thread.sleep(0)

  1. Thread.yield() & Thread.sleep(0) 的实现取决于具体的 JVM 虚拟机,
    • 某些 JVM 可能什么都不做,
    • 而大多数虚拟机会让线程 放弃剩余的 CPU 时间片,重新变为 READY 状态,并放其到 同优先级线程队列的末尾等待cpu资源
    • 但是当我们调用 Thread.yield() 的那一刻,并不意味着当前线程立马释放cpu资源,
      • 这是因为获得时间片的线程从 RUNNING 切换到 READY 仍需要一定的准备时间,这段时间当前线程仍可能运行一小段时间

4) run()

    @Override
    public void run() {
        if (target != null) {
            target.run(); // 若构造函数中初始化了 target,那么会让 target 执行,但是是在当前线程执行,而非新线程
        }
    }

5) start()

    /**
     * 使该线程开始执行; 
     * Java 虚拟机调用该线程的 run 方法。结果是两个线程同时运行:
     * 当前线程(从对 start 方法的调用返回)和另一个线程(执行其 run 方法)。
     * 多次启动一个线程是不合法的。特别是,线程一旦完成执行就可能不会重新启动。
     *
     * @exception  IllegalThreadStateException - 线程已经是 started 状态
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void 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) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

6) interrupt()

    /**
     * 中断这个线程。
     * 除非当前线程正在中断自己,这总是允许的,否则会调用该线程的 checkAccess 方法,这可能会导致抛出 SecurityException。
     *
     * 如果该线程在调用 Object 类的 wait()、wait(long)、 wait(long, int)  | Thread 类的 join()、join(long)、join(long, int) 时被阻塞, 
     * sleep(long), or sleep(long, int),
     * 那么它的中断状态会被清除并且会收到一个InterruptedException。
     *
     * 如果该线程在 InterruptibleChannel 上的 IO 操作中被阻塞,则通道将关闭,线程的中断状态将被重置,
     * 线程将收到 java.nio.channels.ClosedByInterruptException。
     * 如果该线程在 java.nio.channels.Selector 中被阻塞,则该线程的中断状态将被重置,
     * 并且它将立即从 Selector 操作返回,可能返回一个非零值,就像调用了选择器的唤醒方法一样。
     * 如果前面的条件都不成立,则将设置该线程的中断状态。
     *
     * 中断一个不活动的线程不需要有任何效果。
     */
    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();
    }
    
    /**
     * 测试当前线程是否被中断
     * 该方法会清除中断标志位
     * 换句话说,如果该方法被调用两次,第一次清空了中断标志位,第二次再次调用如果期间没有再次被中断,那么肯定返回 false
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    
     /**
     * 测试某个线程是否被中断
     * 中断状态没有被重置
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if this thread has been interrupted;
     * @see     #interrupted()
     * @revised 6.0
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
    
     /**
     * 测试某个线程是否被中断
     * 中断状态是否被重置取决于输入参数 ClearInterrupted
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

7) join()

    /**
     * 最多等待毫秒加上纳秒秒数以使该线程终止。
     * 此实现使用以 this.isAlive 为条件的 this.wait 调用循环。
     * 当线程终止时,将调用 this.notifyAll 方法。
     * 建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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;
            }
        }
    }
    
     /**
     * 判断当前线程是否为活跃状态 - 即线程已经执行了 start() 且没有死亡
     */
    public final native boolean isAlive();

    /**
     * 当前线程最多等待 millis 毫秒 + nanos 纳秒 后执行死亡
     *
     * @param  millis - 等待的毫秒数
     * @param  nanos - 取值范围 ∈ [0, 999999] 的纳秒数
     * @throws  IllegalArgumentException - 毫秒数 < 0 || 纳秒数超范
     * @throws  InterruptedException - 任何线程中断了当前线程
     */
    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);
    }

    /**
     * 等待这个线程死掉。
     * 此方法的调用与调用 join(0) 的行为方式完全相同
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);
    }

7. Thread & Runnable & Callable

1) Thread 创建线程

class Thread implements Runnable {
    // ...
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

// 自定义 Thread 类
class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("六线之首");
    }
}

// 测试类
public class MultiThreads {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("继承 Thread 类创建线程");
        MyThread myThread = new MyThread();
        myThread.start();  
    }
}

2) Runnable 创建线程

@FunctionalInterface // 说明是函数式接口,可用 Lambda 表达式
public interface Runnable {

    public abstract void run();
}

// 自定义 Runnable 类
class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("吞佛童子");
    }
}

// 测试类
public class MultiThreads {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("实现 Runnable 接口创建线程");
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
      }
}

3) Callable 创建线程

  • get阻塞 进程
  • get(long time, TimeUnit unit) 在 有效时间 内阻塞进程,在有限时间内无法读取结果,则返回
@FunctionalInterface
public interface Callable<V> {

    V call() throws Exception;
}

// 自定义 Callable 类
class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("佛剑分说");
        return "宵";
    }

}

// 测试类
public class MultiThreads {
    public static void main(String[] args) throws InterruptedException {
        // Callable 类并没有继承 Thread | 实现 Runnable,因此不能直接使用 new Thread() 代入
        MyCallable myCallable = new MyCallable();
        // FutureTask implements RunnableFuture<V>,而 RunnableFuture<V> extends Runnable, Future<V> 实现 Runnable
        FutureTask futureTask = new FutureTask<>(callableThread);
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
}
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou