2.6 线程的中断
中断一个线程
interrupt() //实例方法
测试当前线程是否中断
interrupted() //静态方法
isInterrupted() //实例方法
以上方法,不是真实的直接中断线程,只是告诉某个线程,需要进行中断,具体是否要中断,由该线程自己来决定
public class Interrupt {
public static void test1() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
}
}
});
t.start();
t.interrupt();
}
public static void main(String[] args) {
test1();
}
}
//中断一个线程,但是线程没有处理中断
public class Interrupt {
public static void test2() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//线程已经中断
while(Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName());
}
}
});
t.start();//t线程的中断标志位 = false
t.interrupt();//t线程的中断标志位 = true
}
public static void main(String[] args) {
test2();
}
}
//运行结果
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
....
1.如果线程调用了 wait/join/sleep
等方法而阻塞挂起,则以 InterruptedException
异常的形式通知,清除中断标志,就是把标志位置为 false
2.否则,只是内部的一个中断标志被设置,Thread
可以通过
(1)Thread.interrupted()
清除标志位
(2)Thread.currentThread().isInterrupted()
不清楚标志位
3.如果线程是运行态时,处理线程中断,需要自行通过判断中断标志位,来进行中断的处理逻辑。
thread.isInterrupted() / Thread.interruped()
public class Interrupt {
public static void test3() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//线程调用了 wait/join/sleep 阻塞时,如果把线程中断掉,会直接抛出一个异常
//阻塞状态时,通过捕获及处理异常,来处理线程的中断逻辑
//抛出异常后,线程中断标志位会重置
try {
System.out.println(Thread.currentThread().isInterrupted());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().isInterrupted());
}
}
});
t.start();
t.interrupt();
}
public static void main(String[] args) {
test3();
}
}
//运行结果:
true
false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at word3.Interrupt$3.run(Interrupt.java:41)
at java.lang.Thread.run(Thread.java:748)
自定义标志位
public class Interrupt {
public static volatile Boolean IS_INTERRUPTED;
public static void test4() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//自定义标志为能满足线程处于运行时的中断操作
//满足不了线程处于阻塞态时的中断操作
while(!IS_INTERRUPTED) {
System.out.println(Thread.currentThread().getName());
}
}
});
t.start();
IS_INTERRUPTED = true;
}
public static void main(String[] args) {
test4();
}
}
3. 线程的状态
线程的状态是一个枚举类型,Thread.State
public class Interrupt {
public static void main(String[] args) {
for(Thread.State state:Thread.State.values()) {
System.out.println(state);
}
}
}
//运行结果
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
4. 线程安全
4.1 观察线程
同时启动20个线程,每次循环10000 次,做++操作
public class UnsafeThread {
public static final int COUNT = 10000;
public static final int NUM = 20;
public static int a = 0;
public static void main(String[] args) {
for (int i = 0; i < NUM; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < COUNT; j++) {
a++;
}
}
}).start();
}
while (Thread.activeCount()>1) {
Thread.yield();
}
System.out.println(a);
}
}
//运行结果:185722
存在线程不安全性
4.2 线程不安全原因
4.2.1原子性
比如:
A-1 、A-2 不具有原子性,而造成在A-1 、A-2操作之间插入了别的代码行,这个代码行通常表现在其他线程 并发/并行 执行的代码行,导致业务逻辑不正确
我们把这个现象叫做同步互斥,表示操作是互斥的
一条Java语句,不一定是原子的,也不一定只是一条指令
比如刚才的代码 a++ ,其实是三步组成的
1.从内存把数据读到CPU
2.进行数据更新
3.把数据写回到CPU
4.2.2 可见性
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < COUNT; j++) {
a++;
//1.从主内存中将a变量复制到工作内存
//2.从工作内存修改变量(+1操作)
//3.将a变量从线程工作内存写回到主内存
}
}
}).start();
造成线程不安全:共享变量发生了修改的丢失