在上一篇中我们介绍了线程的基本概念、生命周期、状态转换以及线程的常规操作
下面再来详细看看线程Thread的部分源码实现
构造器 Thread()
从源码可以看出,构造器内部调用的init函数,具体如下
/**
* Initializes a Thread.
*
* @param g the Thread group 线程组
* @param target the object whose run() method gets called 目标任务
* @param name the name of the new Thread 线程名称
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored. 分配线程方法栈的大小
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
* 是否继承父线程的本地变量
*/
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();
}
}
/* 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);
// 如果需要继承父线程的键值对组合<ThreadLocal, Object>,且该键值对存在
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();
}
sleep
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep 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 static native void sleep(long millis) throws InterruptedException;
使线程进入TIMED_WAITING状态,millis毫秒后自己醒来(不释放锁)
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 纳秒值的取值在1毫秒之内
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
// 类似四舍五入,近似到1毫秒
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
//微调
millis++;
}
//其实最终还是睡眠了millis毫秒,纳秒只是起了微调的作用
sleep(millis);
}
使线程进入TIMED_WAITING状态,至少等待millis毫秒,nanos是一个纳秒级的附加时间,用来微调millis参数(不释放锁) 其实真正经常使用的也就是上面的native方法了
yield
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
yield()也是一个本地方法,注释翻译如下:
- 提示调度器当前线程愿意放弃其当前对处理器的使用。调度器可以随意忽略这个提示。
- yield是一种启发式尝试,用于改善线程之间的相对进程,否则会过度使用CPU。它的使用应该与详细的概要分析和基准测试相结合,以确保它实际上具有预期的效果。
- 很少适合使用这种方法。它可能用于调试或测试目的,在这些目的中,它可能有助于重现由于竞争条件造成的bug。在设计并发控制结构(比如{@link java.util.concurrent.locks)时,它可能也很有用。
join
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @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);
}
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @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");
}
//当传入的时间为0时,即调用join()方法,比如两个线程A,B,
//在A里调用了B.join()方法,A会等B执行完成,A才会继续执行
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
/**
* Waits at most {@code millis} milliseconds plus
* {@code nanos} nanoseconds for this thread to die.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to wait
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value
* of {@code nanos} is not in the range {@code 0-999999}
*
* @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.
*/
//其实这里传入的纳秒和上面sleep有着异曲同工之妙,只是用来微调的
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()方法,使得调用者所在的线程进入WAITING或TIMED_WAITING状态;直到当前线程死亡,或者等待超时之后,再去执行上述调用者线程。
- join方法可以看做是线程间协作的一种方式,很多时候,一个线程的输入可能非常依赖于另一个线程的输出,即join方法一般还可以用于线程间简单的通信,如下
public static void main(String[] args) {
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("开始执行B线程");
try {
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);
System.out.println("B线程执行了" + (i + 1) + "秒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束执行B线程");
}
});
Thread threadA = new Thread() {
@Override
public void run() {
System.out.println("开始执行A");
try {
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束执行A");
}
};
threadA.start();
threadB.start();
}
输出如下:
interrupt
interrupt()方法
/**
注释翻译:
- 1、interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
- 2、如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;
- 3、如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
- 4、如果线程在Selector上被阻塞,select方法将立即返回;
- 5、如果非以上情况,将直接标记 interrupt 状态;
interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的线程,不会受 interrupt 中断
**/
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();
}
写一个例子测试下注释2的内容,如下
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("thread has been interrupt!");
e.printStackTrace();
}
System.out.println("isInterrupted: " + Thread.currentThread().isInterrupted());
System.out.println("task is over");
}
});
thread.start();
thread.interrupt();
}
运行后如下,正好印证了注释2的内容,其他注释可自行测试
静态方法public static boolean interrupted()
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <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);
}
- 测试当前线程是否已中断。此方法清除线程的中断状态。换句话说,如果这个方法被连续调用两次,第二次调用将返回false(除非当前线程再次被中断,在第一次调用清除其中断状态之后,第二次调用检查它之前)。
- 由于线程在中断时没有活动而被忽略的线程中断将通过这个返回false的方法反映出来。 测试如下:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("first :" + Thread.interrupted());
System.out.println("second:" + Thread.interrupted());
System.out.println("task is over");
}
});
thread.start();
thread.interrupt();
运行如下
public boolean isInterrupted()
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <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;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
- 测试此线程是否已被中断。不会清除中断标记位
- 由于线程在中断时没有活动而被忽略的线程中断将通过这个返回false的方法反映出来。 测试如下:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("thread has been interrupt!");
e.printStackTrace();
}
System.out.println("first :" + Thread.currentThread().isInterrupted());
System.out.println("second:" + Thread.currentThread().isInterrupted());
System.out.println("task is over");
}
});
thread.start();
thread.interrupt();
运行如下
本地方法 private native boolean isInterrupted(boolean ClearInterrupted)
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
测试某个线程是否被中断。中断状态是否重置取决于传递的ClearInterrupted的值。 上面两个方法其实调用的是这个本地方法
停止线程
我们知道启动一个线程是start方法,自然有一个对应的终止线程的stop方法,通过stop方法可以很快速、方便地终止一个线程,我们来看看stop的源代码。
通过注解@Deprecated看出stop方法被标为废弃的方法,jdk在以后的版本中可能被移除,不建议大家使用这种API。
那为什么这么好的一个方法怎么不推荐使用,还要标注为废弃呢? 假设有这样的一个业务场景,一个线程正在处理一个复杂的业务流程,突然间线程被调用stop而意外终止,这个业务数据还有可能是一致的吗?这样是肯定会出问题的,stop会释放锁并强制终止线程,造成执行一半的线程终止,带来的后果也是可想而知的,这就是为什么jdk不推荐使用stop终止线程的方法的原因,因为它很暴力会带来数据不一致性的问题。
正因为stop方法太过暴力,所以一般不推荐使用,除非你非常清楚你自己的业务场景,用stop终止不会给你的业务带来影响。
优雅的停止线程
只需要添加一个变量,判断这个变量在某个值的时候就退出循环,这时候每个循环为一个整合不被强行终止就不会影响单个业务的执行结果。
在下一篇我们将继续聊聊线程间的通信方式(wait/notify)以及生产者/消费者的一些介绍