# 十七、 什么是重排序问题
Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序可以保证最终执行的结果是与程序顺序执行的结果一致,并且只会对不存在数据依赖性的指令进行重排序,这个重排序在单线程下对最终执行结果是没有影响的,但是在多线程下就会存在问题。
- 一个例子
```Java
int a = 1;(1)
int b = 2;(2)
int c= a + b;(3)
```
如上c的值依赖a和b的值,所以重排序后能够保证(3)的操作在(2)(1)之后,但是(1)(2)谁先执行就不一定了,这在单线程下不会存在问题,因为并不影响最终结果。
- 一个多线程例子
```Java
public static class ReadThread extends Thread {
public void run() {
while(!Thread.currentThread().isInterrupted()){
if(ready){(1)
System.out.println(num+num);(2)
}
System.out.println("read thread....");
}
}
}
public static class Writethread extends Thread {
public void run() {
num = 2;(3)
ready = true;(4)
System.out.println("writeThread set over...");
}
}
private static int num =0;
private static boolean ready = false;
public static void main(String[] args) throws InterruptedException {
ReadThread rt = new ReadThread();
rt.start();
Writethread wt = new Writethread();
wt.start();
Thread.sleep(10);
rt.interrupt();
System.out.println("main exit");
}
```
如代码由于(1)(2)(3)(4) 之间不存在依赖,所以写线程(3)(4)可能被重排序为先执行(4)在执行(3),那么执行(4)后,读线程可能已经执行了(1)操作,并且在(3)执行前开始执行(2)操作,这时候打印结果为0而不是4.
解决:使用volatile 修饰ready可以避免重排序。
# 十八、 什么是中断
Java中断机制是一种线程间协作模式,通过中断并不能直接终止另一个线程,而是需要被中断的线程根据中断状态自行处理。
例如当线程A运行时,线程B可以调用A的 interrupt()方法来设置中断标志为true,并立即返回。设置标志仅仅是设置标志,线程A并没有实际被中断,会继续往下执行的,然后线程A可以调用isInterrupted方法来看自己是不是被中断了,返回true说明自己被别的线程中断了,然后根据状态来决定是否终止自己活或者干些其他事情。
Interrupted经典使用代码
```Java
public void run(){
try{
....
//线程退出条件
while(!Thread.currentThread().isInterrupted()&& more work to do){
// do more work;
}
}catch(InterruptedException e){
// thread was interrupted during sleep or wait
}
finally{
// cleanup, if required
}
}
```
``` 使用场景: ```
- 故意调用interrupt()设置中断标志,作为线程退出条件
```Java
public static class MyThread extends Thread {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("do Someing....");
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(1000);
t.interrupt();
}
```
- 当线程中为了等待一些特定条件的到来时候,一般会调用Thread.sleep(),wait,join方法在阻塞当前线程,比如sleep(3000);那么到3s后才会从阻塞下变为激活状态,但是有可能在在3s内条件已经满足了,这时候可以调用该线程的interrupt方法,sleep方法会抛出InterruptedException异常,线程恢复激活状态。
```java
public static class SleepInterrupt extends Object implements Runnable{
public void run(){
try{
System.out.println("thread-sleep for 2000 seconds");
Thread.sleep(2000000);
System.out.println("thread -waked up");
}catch(InterruptedException e){
System.out.println("thread-interrupted while sleeping");
return;
}
System.out.println("thread-leaving normally");
}
}
public static void main(String[] args) throws InterruptedException {
SleepInterrupt si = new SleepInterrupt();
Thread t = new Thread(si);
t.start();
//主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
try {
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("main() - interrupting other thread");
//中断线程t
t.interrupt();
System.out.println("main() - leaving");
}
```
``` InterruptedException的处理 ```
如果抛出 InterruptedException那么就意味着抛出异常的方法是阻塞方法,比如Thread.sleep,wait,join。
那么接受到异常后如何处理的,醉简单的是直接catch掉,不做任何处理,但是中断发生一般是为了取消任务或者退出线程来使用的,所以如果直接catch掉那么就会失去做这些处理的时机,出发你能确定不需要根据中断条件做其他事情。
- 第一种方式 catch后做一些清理工作,然后在throw出去
- 第二种方式 catch后,重新设置中断标示