1.中断一个线程

结束可能有两种情况:

  1. 已经把任务执行完了
  2. 任务执行了一半,被强制结束

1.1 第一种方式

public void run() {
                while (!isQuit){
                    System.out.println("我在进行转账");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("转账操作被终止");
            }

上面这种结束方式比较温和,当标记位被设置上以后,等到当前这次循环执行完后,再结束线程

java 中断执行 java程序中断_java 中断执行

这种方式比较温和,当标记位被设置上之后,等到当前这次循环执行完之后,再结束线程。例如当前线程执行到 sleep 的时候,已经 sleep  100ms 了,此时 isQuit 被设置为 true,当前线程不会立刻退出,而是会继续 sleep ,把剩下的 400ms sleep 完 才会结束线程。

sleep 表示休眠,只要执行到这个代码,此时就会停下来,等到时间到(0.5s)后才会继续走,谁调用 sleep 就会停下来

java 中断执行 java程序中断_System_02

1.2 第二种方式

public class ThreadDemo11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(){
            @Override
            public void run(){
                //此处直接使用线程内部的标记位来判定
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("正在转账,请勿打扰!");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                      //break;
                    }
                }
                System.out.println("转账已被终止!");
            }
        };
        t.start();

        Thread.sleep(5000);
        System.out.println("对方是内鬼,赶快终止交易!");
        t.interrupt();
    }
}

 

运行上面的代码,会发现,程序没有被终止,而是报出了异常而这个异常,正是代码中出现的可能会抛出的异常

java 中断执行 java程序中断_System_03

 

情况1:

(当前线程在 sleep / wait 等方法中阻塞时),下面的操作本质上就是给该线程触发一个异常

java 中断执行 java程序中断_System_04

此时线程内部就会收到这个异常,具体针对这个异常如何处理,这个 catch 内部的事情

java 中断执行 java程序中断_主线程_05

正确的操作就是在异常里面加一个 break 来终止循环

java 中断执行 java程序中断_java 中断执行_06

 

情况2:

(当前线程不在某个 sleep / wait 中阻塞)

这个操作会给线程的  Thread.currentThread().isInterrupted()  置位 true 

这两个动作是同时执行的

 

 更优先考虑第一种比较温和的方式,更容易保证原子性(虽然没有事务哪里要求那么高,第二种方式的好处是更及时

 

1.2.1 两种中断标记

  1. Thread.interrupted() 判断当前线程的中断标志被设置   清除中断标志
  2. Therad.currentThread().isInterrupted() 判断线程的中断标志被设置 不清除中断标志位

中断标记想象成是一个 boolean 值,初始情况是 false。

调用 interrupt,此时就把这个中断标记设置为 true,Thread.interrupted() 来判断中断标记的话,返回结果为 true,同时会把中断标记设回 false;Thread.current().isInterrupted() 判定中断标记,返回结果一直是 true。第一种方式就像闹钟的直接结束闹钟,闹钟状态就设成 false,第二种就类似于稍后继续,闹钟一会儿还会响,闹钟状态仍是 true。

第一种方式:

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.interrupted());
                }

            }
        };
        t.start();

        t.interrupt();
    }
}

运行结果:

java 中断执行 java程序中断_System_07

t.interrupt 被调用的时候,Thread.interrupted() 就能感知到,感知到一次之后,标记位就被清楚了

 

第二种方式:

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().isInterrupted());
                }

            }
        };
        t.start();

        t.interrupt();
    }
}

运行结果:

java 中断执行 java程序中断_System_08

仅仅是判定标记位,而不是修改标记位


2.线程等待

线程之间是并发执行的关系

多个线程之间,谁先执行,谁后执行,谁执行到哪里让出 CPU……作为程序员是无法完全感知的,全权由系统内核负责,例如创建一个新线程的时候,此时接下来是主线程继续执行还是新线程继续执行是不好保证的

通过下面的代码来查看打印的执行顺序:

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run(){
                while (true){
                    System.out.println("我是新线程");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        };
        t.start();
        while (true){
            System.out.println("我是主线程");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

 

运行结果:

java 中断执行 java程序中断_System_09

观看运行结果发现,多种情况都有出现,没有固定顺序

 

虽然我们没办法控制哪个线程先运行那个后运行,但是可以控制让哪个线程先结束,哪个线程后结束(借助线程等待)

2.1 join方法

执行 join 方法的线程就会阻塞,一直阻塞到对应线程执行结束之后才会执行【存在的意义就是为了控制线程结束的先后顺序】

public class ThreadDemo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 3; i++) {
                    System.out.println("我是线程1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t2 = new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 3; i++) {
                    System.out.println("我是线程2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t1.start();
        t1.join();//join 的效果是等待线程结束,当执行这行代码时,程序就阻塞了,一直阻塞到 t1 结束才会继续
        t2.start();
        t2.join();
    }

}

运行效果

java 中断执行 java程序中断_System_10

如果线程结束了,才调用 join ,此时 join 也会立刻返回

 

多线程的一个场景:

例如要进行一个复杂的计算,主线程把任务分成几份,每个线程计算自己一份的任务,当所有任务都被分别结算完毕之后,主线程再来汇总(就必须要保证主线程是最后执行完的线程)

【举个例子】

领导拆分任务,把任务分给手下来做,某个下属执行完了就找领导汇报,汇报完了就可以下班了,领导必须要等到所有小弟都汇报完了,汇总完成以后才可以下班

 

3.线程休眠

java 中断执行 java程序中断_ide_11

如果线程在正常运行计算判断逻辑,此时就是在就绪队列中排队,调度器就会让就绪队列中筛选出合适的 PCB 让他上 CPU 执行。

如果某个线程调用 sleep 就会让对应的线程的 PCB 进入到阻塞队列【类似于被打入冷宫的妃子】。

进入阻塞队列是没有办法上 CPU 执行的。

进入阻塞队列的方式有好几种,对于 sleep (进入冷宫)的时间是有限制,时间到了之后,就自动被系统把这个 PCB 拿回到原来的就绪队列中,像 join、wait、锁也是可能导致线程被阻塞的,每一种方式恢复的条件都不一致。

各种休眠方式线程被恢复的方法:

  • join 被恢复的条件就是对应的线程结束
  • wait 等的就是一个 notify 通知
  • 锁是等待其他线程把锁释放。