🟤 Thread
2022年6月20日
- Java
🟤 Thread
1. 类注释
- JVM 允许多线程并发执行
- 每个线程都有用一个优先级:优先级越高说明线程越容易被执行
- 每个线程可以被设置为 守护线程
- 若某个线程里面又创建了一个新的线程,那么这个新线程和父线程有相同的优先级,且若父线程为 守护线程,则该新线程也为守护线程
- 当 JVM 运行时,通常有一个
main
的非守护线程 - JVM 会继续运行,直到有以下某种情况发生:
- 在 Runtime 期间执行
exit()
- 所有 非守护线程 均已执行完毕,要么执行 run() 后正常返回,要么在 run() 中抛出异常
- 在 Runtime 期间执行
- 创建新线程的 2 种方式:
- 创建一个类
extends Thread
,并重写里面的run()
方法 - 创建一个类
implements Runnable
,并重写里面的run()
方法
- 创建一个类
- 每个线程都有一个名字用于区分,也许有多个线程拥有同一个名字
- 如果一个线程在创建的时候没有指定名称,则会被自动分配一个名称
2. 类图
public class Thread implements Runnable {
// ...
}
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)
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());
}