线程的交互及其相关
线程的中断
因为线程中的stop()方法,已经被弃用,因为它是不安全的,使用它相当于你在家用电脑,直接在没有预知的情况下直接给你停电了一样,太暴力,所以不能用它。应该用 interrupt(),这个不是直接中断,而是将当前线程标志为中断标记,只是一个标记。怎么用,代码示例如下:
1 package com.huolongluo.coindemo.thread; 2 3 /** 4 * Created by 火龙裸 on 2019/11/13. 5 * desc : 线程的中断 6 * version: 1.0 7 */ 8 public class ThreadDemo { 9 public static void main(String[] args) { 10 MyThread myThread = new MyThread(); 11 myThread.start();//启动子线程 12 13 try { 14 Thread.sleep(500);//当前工作的那个线程(主线程)沉睡500毫秒(也就是让子线程跑500毫秒钟) 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 // myThread.stop();//不安全,已被弃用 19 myThread.interrupt();//主线程沉睡500毫秒后,继续在主线程当中,通过“myThread”对象,将子线程标记为中断, 20 } 21 } 22 23 class MyThread extends Thread { 24 @Override 25 public void run() { 26 for (int i = 0; i < 1000000; i++) { 27 //(Thread.interrupted()返回当前线程(这个代码所运行的那个线程)标志位(当前代码所运行在的那个线程状态),并将标志位重置 28 if (Thread.interrupted()) {//也可以使用isInterrupted(),判断当前线程(这个代码所运行的那个线程)标志位,但isInterrupted()不会重置标志位 29 //收尾工作 30 return; 31 } 32 System.out.println("number: " + i); 33 } 34 } 35 }
注意:Thread.interrupted()是个静态方法,和直接使用isInterrupted()是有区别的。虽然都会返回当前代码执行的那个线程的线程状态,但是Thread.interrupted()还会同时将标志位重置为false。
另外一种情况,当子线程有沉睡/等待操作,主线程当中通过子线程对象调用了interrupt()方法,将子线程的线程标志为标记为中断,因为子线程有睡眠,所以会先抛出子线程的异常,而不是执行判断Thread.interrupted()判断,这个时候所以收尾工作应该在子线程中的e.printStackTrace();之后,子线程所抛出的InterruptedException异常,也会将自己的线程中断状态标志位重置为false,所以要是没有在子线程的e.printStackTrace()之后做收尾工作,相当于没有中断,仍旧会一直循环执行下去。因为这个时候子线程在休眠,为了避免浪费资源,收尾工作需要在子线程抛异常的那个位置。
代码示例如下:
1 package com.huolongluo.coindemo.thread; 2 3 /** 4 * Created by 火龙裸 on 2019/11/13. 5 * desc : 线程的中断 6 * version: 1.0 7 */ 8 public class ThreadDemo { 9 public static void main(String[] args) { 10 MyThread myThread = new MyThread(); 11 myThread.start();//启动子线程 12 13 try { 14 Thread.sleep(500);//当前工作的那个线程(主线程)沉睡500毫秒(也就是让子线程跑500毫秒钟) 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 // myThread.stop();//不安全,已被弃用 19 myThread.interrupt();//主线程沉睡500毫秒后,继续在主线程当中,通过“myThread”对象,将子线程标记为中断, 20 } 21 } 22 23 class MyThread extends Thread { 24 @Override 25 public void run() { 26 for (int i = 0; i < 1000000; i++) { 27 //(Thread.interrupted()返回当前线程(这个代码所运行的那个线程)标志位(当前代码所运行在的那个线程状态),并将标志位重置 28 if (Thread.interrupted()) {//也可以使用isInterrupted(),判断当前线程(这个代码所运行的那个线程)标志位,但isInterrupted()不会重置标志位 29 System.out.println("判断标志位"); 30 //收尾工作 31 return; 32 } 33 try { 34 Thread.sleep(2000);//当前子线程沉睡2秒 35 } catch (InterruptedException e) {//InterruptedException异常,也会将标志位重置 36 e.printStackTrace(); 37 //收尾工作 38 System.out.println("子线程 收尾啦"); 39 return; 40 } 41 System.out.println("number: " + i); 42 } 43 } 44 }
运行结果:
还有一种,睡眠方式:SystemClock.sleep(2000),它与Sleep(2000)的区别是,SystemClock.sleep(2000)把InterruptedException异常给吃掉了,它不会抛异常,所以也不会重置标志位,只是单纯的让当前线程睡2秒钟。示例代码如下:
运行结果:
只打印一行,然后再return结束掉了。
线程的等待
线程交互中,也经常会用到等待wait(),但其实wait()方法和notifyAll()方法都是一个Object类中的方法。代码示例如下:
1 package com.huolongluo.coindemo.thread; 2 3 /** 4 * Created by 火龙裸 on 2019/11/16. 5 * desc : 线程等待wait() 6 * version: 1.0 7 */ 8 public class ThreadDemo2 { 9 public static void main(String[] args) { 10 WaitDemo waitDemo = new WaitDemo(); 11 waitDemo.runTest(); 12 } 13 } 14 15 interface TestDemo { 16 void runTest(); 17 } 18 19 class WaitDemo implements TestDemo { 20 private String str; 21 22 @Override 23 public void runTest() { 24 Thread thread1 = new Thread(new Runnable() { 25 @Override 26 public void run() { 27 try { 28 Thread.sleep(1000); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 initStr();//初始化字符串 33 } 34 }); 35 thread1.start(); 36 37 Thread thread2 = new Thread(new Runnable() { 38 @Override 39 public void run() { 40 try { 41 Thread.sleep(500); 42 } catch (InterruptedException e) { 43 e.printStackTrace(); 44 } 45 printStr();//打印字符串 46 } 47 }); 48 thread2.start(); 49 } 50 51 private synchronized void initStr() { 52 str = "火龙裸"; 53 System.out.println("初始化完成"); 54 notifyAll(); 55 } 56 57 private synchronized void printStr() { 58 while (str == null) { 59 System.out.println("循环中"); 60 try { 61 wait();//释放锁,进入到等待队列 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 } 66 System.out.println("打印结果: " + str); 67 } 68 }
运行结果:
结果正是我想要的。但是假如在printStr()方法中,去掉wait()方法,其他代码不动,则会出现永远一直打印“循环中”,就算直到1秒后,也不会执行“初始化initStr()方法”。因为那个monitor,锁一直被thread2所持有没被释放。这里面调用wait()方法后,线程是进入到等待队列(先进先出),被唤醒后,依旧排队执行,以队列的形式,排在唤醒(主叫)的那个线程后面。还有wait()方法和notifyAll()方法都是必须要写到synchronized里面,要是没有被synchronized包裹,去执行的时候,会报错,所以必须是这样写。