Thread状态
Thread内部有个State枚举,标示着线程的状态。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
- NEW 状态是指线程刚创建, 尚未启动
- RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等
- BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区
- WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
- TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态
- TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)
构造方法
构造函数及其几个相关的成员变量
/* 带目标run对象. */
private Runnable target;
/* 线程组 */
private ThreadGroup group;
/* 此线程的类加载器 */
private ClassLoader contextClassLoader;
/* 想要的栈大小,为0时此参数被忽略,且有VM不支持此参数 */
private long stackSize;
/* 状态标识,0代表新建未开始*/
private volatile int threadStatus = 0;
构造函数很多,最终都调init方法
/**
* Initializes a Thread.
*
* @param g 线程组
* @param target 要执行的带run的目标对象
* @param name 线程名
* @param stackSize 新线程的栈大小,等于0时可忽略此参数
* @param acc 接入控制上下文
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
//获取线程组
if (g == null) {
/* 从SecurityManager拿线程组 */
if (security != null) {
g = security.getThreadGroup();
}
/* 如果还没拿到从当前线程拿*/
if (g == null) {
g = parent.getThreadGroup();
}
}
/* 检查是否可获取 */
g.checkAccess();
//还是权限控制检查
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
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
/* 设置线程ID */
tid = nextThreadID();
}
启动start
/* 线程启动方法 */
public synchronized void start() {
/**
* 如果线程不是NEW状态,则抛异常
*/
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) {
/*什么都不做,如果start0排除一个异常,它已经可以被调用栈知道 */
}
}
}
private native void start0();
start0方法是native实现,在真正开始之前校验了状态,并接入ThreadGroup管理。真正的运行的是目标类的run方法
/* Runnale 接口的方法*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
join方法
举例:t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续
*如果这个线程还活着就一直等待*/
public final void join() throws InterruptedException {
join(0);
}
/*如果这个线程还活着就一直等待millis时间*/
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;
}
}
}
它被synchronized标记,实现主要是自旋方法检验t线程是否活着,如果活着,则主线程(调用线程)wait释放锁,指导t线程结束。
interrupt方法
每个线程都有一个与之相关联的 Boolean 属性,用于表示线程的中断状态(interrupted status)。中断状态初始时为 false;当另一个线程通过调用 Thread.interrupt() 中断一个线程时,会出现以下两种情况之一。如果那个线程在执行一个低级可中断阻塞方法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那么它将取消阻塞并抛出 InterruptedException。否则, interrupt() 只是设置线程的中断状态。 在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情。中断状态可以通过 Thread.isInterrupted() 来读取,并且可以通过一个名为 Thread.interrupted() 的操作读取和清除。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
//如果被可中断IO阻塞,走此逻辑
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 仅仅设置interrupt标识位
b.interrupt(this);
return;
}
}
interrupt0();
}
//当前线程是否被中断,并清空中断状态(连续两次调用,第二次一定返回false)
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//当前线程是否被中,根据true,false来是否重置中断zhuangt
private native boolean isInterrupted(boolean ClearInterrupted);
//当前线程是否被中断,不修改中断状态
public boolean isInterrupted() {
return isInterrupted(false);
}
中断是一种协作机制。当一个线程中断另一个线程时,被中断的线程不一定要立即停止正在做的事情。相反,中断是礼貌地请求另一个线程在它愿意并且方便的时候停止它正在做的事情。有些方法,例如 Thread.sleep(),很认真地对待这样的请求,但每个方法不是一定要对中断作出响应。对于中断请求,不阻塞但是仍然要花较长时间执行的方法可以轮询中断状态,并在被中断的时候提前返回。 您可以随意忽略中断请求,但是这样做的话会影响响应。
中断的协作特性所带来的一个好处是,它为安全地构造可取消活动提供更大的灵活性。我们很少希望一个活动立即停止;如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态。中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止。
总结
wait()和notify/notifyAll()方法
wait()方法
- 线程进入WAITING状态,并且释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁,等待其他线程调用“锁标志“对象的notify或notifyAll方法恢复
- wait方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的,所以调用wait方式时必须获取到monitor对象的所有权即通过Synchronized关键字,否则抛出IllegalMonitorStateException异常
notify/notifyAll()方法
- 在同一对象上去调用notify/notifyAll方法,就可以唤醒对应对象monitor上等待的线程了。notify和notifyAll的区别在于前者只能唤醒monitor上的一个线程,对其他线程没有影响,而notifyAll则唤醒所有的线程
sleep/yield/join方法
sleep
- sleep方法的作用是让当前线程暂停指定的时间(毫秒)
- wait方法依赖于同步,而sleep方法可以直接调用
- sleep方法只是暂时让出CPU的执行权,并不释放锁。而wait方法则需要释放锁
yield
- yield方法的作用是暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止
- yield只能使同优先级或更高优先级的线程有执行的机会
join
-
等待调用join方法的线程结束,再继续执行。如:t.join(),主要用于等待t线程运行结束
-
作用是父线程等待子线程执行完成后再执行,换句话说就是将异步执行的线程合并为同步的线程
stop() -
因为调用stop会使线程释放所有的锁,导致不安全情况,在调用stop时候,由锁保护的临界区可能处于状态不一致的情况,这不一致状态将暴露给其他线程
-
推荐的做法是,维护一个状态变量,当线程需要停止时更改这一状态变量,该线程应检查这一状态变量,看该线程是否应该终止了
关于interrupt()中断函数
- 其实调用这个函数并不是真的中断线程,这个函数只是将Thread中的interrupt标志设置为true,用户需自行检测这一变量,停止线程,这种做法避免了stop带来的问题