并发之线程以及线程的中断状态
什么是线程:对于现在的计算机而言,一般都是多核处理器;对于一个程序而言,在宏观上来说可以同时执行多个任务;那么每一个任务可以称之为一个线程,而每一个程序可以称之为一个进程;并发执行的进程数目并不是由CPU的数目决定的,操作系统将CPU的时间片分配给每一个进程,在宏观上来说给人一种并行处理的感觉;线程和进程最大的区别就是每一个进程拥有自己独享的变量,而线程之间却是共享变量,尽管多线程共享变量会带来种种并发安全问题,但是Java程序对于线程安全问题的处理已经很完善了。
在讲述线程的中断状态之前,我们先来看一看JDK中对于线程的状态做了哪一些的定义:
public enum State {
/**
* 线程的新建状态,线程没有开始运行
*/
NEW,
/**
* 线程的可运行状态,该状态表示可以在JVM中运行但同时也有可能等待来自操作系统对其他资源的处理
*/
RUNNABLE,
/**
* 线程的阻塞状态,该状态下的线程正在等待同步锁,试图进入同步方法或者同步代码块或者重入锁的同步方法或者代码块
*/
BLOCKED,
/**
* 线程的等待状态,该状态的下的线程同样会一直等待下去,直到有其他线程将其唤醒,否则会陷入一直的等待状态
*/
WAITING,
/**
* 线程的计时等待状态,当时间到了之后并不能进入可运行状态,只是有机会获取对象的锁
*/
TIMED_WAITING,
/**
* 线程的终止状态
*/
TERMINATED;
}
接下来我们看看三个方法:
1 boolean isInterrupted();表示判断该线程是否处于中断位;如果处于中断位置,返回true,否则返回false;默认情况下处于非中断位,返回false;(源码如下)
public boolean isInterrupted() {
return this.isInterrupted(false);
}
private native boolean isInterrupted(boolean var1);
2 void interrupt();将当前线程的中断位进行标记;表示该线程的中断位置为true;
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 仅仅将当前线程的中断位进行标记
b.interrupt(this);
return;
}
}
interrupt0();
}
3 static boolean interrupted();调用该方法,就会清除线程的中断状态
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
看一段代码的实例:
package com.temps.service.impl;
/**
* @Authgor: gosaint
* @Description:
* @Date Created in 11:04 2018/5/30
* @Modified By:
*/
public class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("Test Thread!");
}
public static void main(String[] args) {
Thread t=new ThreadTest();
/**
* 获取线程的状态
*/
System.out.println("线程的状态为"+t.getState());
/** 中断线程*/
t.interrupt();
System.out.println(t.isInterrupted());
}
}
上述代码我们创建了一个线程,但是并没有启动该线程;理论上来说该线程处于新建状态;调用了interrupt()方法之后,会出现什么情况;
结果就是线程的中断标志位是false;不会发生任何的变化;在《java core》中说:没有任何语言方面的需求要求一个中断的线程被终止;照这样说来,线程的终止状态即使调用了interrupt()方法,本身也不会发生任何的变化;同样的新建状态也不会发生任何的变化!
下面的程序创建并启动了一个新的线程,然后调用join()方法,让该线程执行完毕,此时调用interrupt()方法,显示的结果如下所示:
package com.temps.service.impl;
/**
* @Authgor: gosaint
* @Description:
* @Date Created in 11:04 2018/5/30
* @Modified By:
*/
public class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("Test Thread!");
}
public static void main(String[] args) {
Thread t=new ThreadTest();
t.start();
try {
t.join();
System.out.println("线程的状态为"+t.getState());
/** 中断线程*/
t.interrupt();
System.out.println(t.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如果线程处于运行状态,那么该线程的状态就是RUNNABLE,但是不一定所有处于RUNNABLE状态的线程都能获得CPU运行,在某个时间段,只能由一个线程占用CPU,那么其余的线程虽然状态是RUNNABLE,但是都没有处于运行状态。而我们处于RUNNABLE状态的线程在遭遇中断操作的时候只会设置该线程的中断标志位,并不会让线程实际中断,想要发现本线程已经被要求中断了则需要用程序去判断。例如:
package com.temps.service.impl;
/**
* @Authgor: gosaint
* @Description:
* @Date Created in 11:04 2018/5/30
* @Modified By:
*/
public class ThreadTest extends Thread{
@Override
public void run() {
while (true){
//System.out.println("Test Thread---------!");
}
}
public static void main(String[] args) {
Thread t=new ThreadTest();
t.start();
try {
System.out.println("线程的状态为"+t.getState());
/** 中断线程*/
t.interrupt();
Thread.sleep(1000);//等待线程中断完毕
System.out.println(t.isInterrupted());
System.out.println("线程的状态为end="+t.getState());
} catch (Exception e) {
e.printStackTrace();
}
}
}
尽管调用interrupt()方法并不能中断线程的执行,但是在可运行状态下的线程可以改变线程的中断位标志,此时我们可以用自己的代码来控制,比如说
if(Thread.currentThread.isInterrupt()==true){
//退出JVM
}
对于线程的阻塞状态和线程的可运行状态基本是相同的;
package com.temps.service.impl;
/**
* @Authgor: gosaint
* @Description:
* @Date Created in 11:04 2018/5/30
* @Modified By:
*/
public class ThreadTest extends Thread{
@Override
public void run() {
doSomething();
}
public synchronized static void doSomething(){
while(true){
//do something
}
}
public static void main(String[] args) throws Exception{
Thread t1=new ThreadTest();
t1.start();
Thread t2=new ThreadTest();
t2.start();
Thread.sleep(1000);
System.out.println(t1.getState());
System.out.println(t2.getState());
System.out.println("0000000000000000000");
t2.interrupt();
System.out.println(t2.isInterrupted());
System.out.println(t2.getState());
}
}
创建了两个线程,并且按照顺序启动;在run() 方法中调用了同步方法,并且是死循环的状态,此时调用的线程就会永久的持有同步锁,那么其他的线程只能陷入无尽的等待锁的状态中,陷入阻塞状态,调用结果很清楚的说明了这一点;
我们再来看看等待状态和计时等待状态的状况:
package com.temps.service.impl;
/**
* @Authgor: gosaint
* @Description:
* @Date Created in 11:04 2018/5/30
* @Modified By:
*/
public class ThreadTest extends Thread{
@Override
public void run() {
synchronized (this){
try {
wait();
} catch (InterruptedException e) {
System.out.println("线程中断异常!");
}
}
}
public static void main(String[] args) throws Exception{
Thread t1=new ThreadTest();
t1.start();
Thread.sleep(5000);
System.out.println(t1.getState());
t1.interrupt();
Thread.sleep(1000);
System.out.println(t1.isInterrupted());
}
}
创建一个线程,然后启动线程,在run()方法中进入等待状态;之后再调用interrupt()方法,检测中断状态
综上所述,我们分别介绍了不同种线程的不同状态下对于中断请求的反应。NEW和TERMINATED对于中断操作几乎是屏蔽的,RUNNABLE和BLOCKED类似,对于中断操作只是设置中断标志位并没有强制终止线程,对于线程的终止权利依然在程序手中。WAITING/TIMED_WAITING状态下的线程对于中断操作是敏感的,他们会抛出异常并清空中断标志位。
当上帝未揭开人类的未来图景之前,人类最伟大的智慧存在于两个词中,“等待”和“希望”