Thread的六种状态
-
NEW
Thread对象被创建,也即被初始化后,且未执行start()函数之前的状态 -
RUNNABLE
Thread对象调用了start()函数后,但是还未执行run()方法,例如它正在等待操作系统为其分配处理器时间片,此处又涉及了操作系统的调度知识 -
BLOCKED
等待获取锁对象的状态,在这种状态下,线程一直等待着获取锁对象然后进入同步方法或是同步代码块。进入BLOCKED状态的途径有:调用了wait()函数等待重新获取锁,或是在调用wait()函数之前就未获取到锁。一个是曾经拥有了,但是又放弃了,一个是未曾拥有过 -
WAITING
调用了Object.wait()函数或是Thread.join()函数或是LockSupport.park()函数后的状态,处于此种状态下的线程,在等待其他线程调用Object.notify()或是Object.notifyAll()。当调用了Thread.join()函数后,只能等待这个线程被terminate或是执行完成,这个过程即为WAITING -
TIMED_WAITING
和WAITING类似,只是这个增加了一个时间参数 -
TERMINATED
线程终止的状态,当线程执行完以后,则进入这个状态。
BLOCKED和WAITING的区别
BLOCKED状态是线程阻塞在等待锁以及阻塞在进入或是重新进入同步代码块或是同步方法,例如调用了wait()函数;
而WAITING也同样是调用了wait()或是join()等方法,其实状态也是放弃了已经获取的CPU资源,等着其他线程调用notify()或是notifyAll()等方法;
难道它们的区别是:BLOCKED是等待进入同步代码或同步块去执行,WAITING是等待被唤醒?
网上多数的说法是:BLOCKED状态相关联的是同步队列,WAITING相关联的是等待队列,线程的状态变化是:由WAITING状态变化为BLOCKED状态,BLOCKED状态在等到锁后即进入RUNNABLE状态,也可以理解为BLOCKED和WAITING有一个时序的关系。
调用线程的join()函数,为什么会阻塞当前线程执行
线程是由Java虚拟机来创建和执行,并且Thread的run()方法实际上是由Java虚拟机来负责调度,run()方法运行于创建的线程中。
除了run()方法之外,Thread类本身和其他普通类没有多大区别,Thread类中的其他方法和普通类的方法也没有多大区别,对Thread类其他方法的调用都是运行于当前调用线程的虚拟机栈(也即当前调用线程中)
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread("JoinThread");
myThread.start();
myThread.join();
System.out.println("Hello World");
}
在这种状态下,不会输出 Hello World,直到MyThread线程的run()函数执行完毕,线程退出,否则程序一直阻塞于myThread.join()函数
除了在线程中run()方法中的代码段,其他在当前线程中调用的代码段运行于当前线程(多进程,远程方法调用等除外,此处可以忽略)
myThread.start()函数执行后,MyThread线程执行自己的业务,执行逻辑立即回到main()函数线程
而随后myThread.join()函数是由当前线程,也即main()函数线程调用,myThread.join()函数运行于main()函数线程,而join()函数会持久阻塞,所以main()函数线程不会继续执行,会一直阻塞于join()函数的while循环中,直到myThread线程执行完毕后退出线程,此时join()的while循环也执行结束,main()函数线程从join()函数返回,继续执行下面的代码。
join()函数介绍
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) {
// 如果join()函数的参数是0,则说明调用者想一直等待此线程执行结束
while (isAlive()) {
// 如果线程一直alive,则一直处于while循环中,直到此线程非alive才退出循环
wait(0);
}
} else {
while (isAlive()) {
/**
* 如果传递的参数大于0,则判断当前时间减去join()函数初次调用的时间
* 是否大于需要等待的时间,如果大于需要等待的时间,则说明等待时间够了,执行break退出循环
* 如果是不大于需要等待的时间,则说明还得继续等待,while循环不能break
*/
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
// while循环退出,则join()函数也执行完退出
}
}
join()函数运行于调用方的线程,join()函数一直阻塞于while循环中,所以调用方必须等待join()函数返回后才可执行接下来的代码,所以它才会阻塞调用方,让调用方等着它执行完成后才会执行自己的业务,这也就实现了线程插队的功能。
归根结底还是看代码段运行于哪个线程
interrupt()
interrupt()函数并不能真正结束一个线程,其只是为调用此函数的线程打上一个interrupt的标签,其函数内部会调用interrupt0(),此函数是一个native函数,api中有明确的说明:// Just to set the interrupt flag。它并不能中断一个线程,只是设置一个线程中断标志。
既然interrupt()只是设置中断标志,又没有实际作用,那这个函数有什么用呢?
这个函数其实是和sleep(), wait()等函数共同使用的,具体可参考<https://www.cnblogs.com/jiangzhaowei/p/7201244.html>
interrupted()
此函数为一个静态函数,它的作用是判断执行这段代码的线程是否处于中断状态,这个函数具体清除中断状态的作用,例如:
Thread.currentThread().interrupt();
System.out.println("Thread.interrupted() = " + Thread.interrupted());
System.out.println("Thread.interrupted() = " + Thread.interrupted());
输出的结果为:
Thread.interrupted() = true
Thread.interrupted() = false
当执行第一段Thread.interrupted()时,获取到interrupt状态,即为true,同时它又会把当执行此段代码的线程的interrupt状态重置为false状态,
所以在执行第二段Thread.interrupted()时,获取到interrupt状态是已经被重置之后的状态,即为false。
isInterrupted()
此函数为Thread类的一个实例函数,这个函数是被Thread类的对象调用,它的作用域为某一实例对象。
当Thread类对象执行interrupt()函数设置interrupt状态后,再调用isInterrupted()函数并不会重置interrupt状态,因此每次调用isInterrupted()都会返回线程的真实状态。
interrupted()和isInterrupted()的区别
- interrupted()属于静态方法,isInterrupted()属于实例方法
- interrupted()会清除interrupt状态,isInterrupted()不会清除interrupt状态
- interrupted()的返回值为调用此函数的线程的中断状态,isInterrupted()的返回值是某一线程对象的中断状态。
volatile关键字
volatile可以实现轻量级同步,例如一个线程写,多个线程读,涉及到多线程同时读写时,其不能保证同步。
volatile也能够禁止指令优化,从而避免了多线程环境下程序出现乱序执行的现象。
在访问被volatile修饰的变量时,会强制从主内存中获取最新的数据,这样并不会实现线程同步,因为其他线程使用volatile修饰的变量时会它加载到线程私有内存空间中计算,计算结束后会把数据更新到主内存。