Java多线程编程(七)线程状态、线程组与异常处理
一、线程的状态
线程对象在不同的运行时期有不同的状态,状态信息就存在于State枚举类中。
调用与线程有关的方法后,会进入不同的线程状态,这些状态之间某些是可双向切换的,比如WAITING和RUNNING状态之间可以循环地进行切换;而有些是单向切换的,比如线程销毁后并不能自动进入RUNNING状态。
1.验证NEW、RUNNABLE和TERMINATED
NEW状态时线程实例化后还从未执行start()方法时的状态,而RUNNABLE状态是线程进入运行的状态,TERMINATED是线程被销毁时的状态。
示例:从输出结果可以看出,第一行打印main主线程的状态为RUNNABLE,然后t线程在还没start之前呈NEW状态,执行start后t线程是RUNNABLE状态,在线程执行完后t线程被销毁,进入TERMINATED状态。
package extthread; public class MyThread extends Thread { public MyThread() { System.out.println("构造方法中的状态:" + Thread.currentThread().getState()); } @Override public void run() { System.out.println("run方法中的状态:" + Thread.currentThread().getState()); } }
package test; import extthread.MyThread; public class Run { // NEW, // RUNNABLE, // TERMINATED, // BLOCKED, // WAITING, // TIMED_WAITING, public static void main(String[] args) { try { MyThread t = new MyThread(); System.out.println("main方法中的状态1:" + t.getState()); Thread.sleep(1000); t.start(); Thread.sleep(1000); System.out.println("main方法中的状态2:" + t.getState()); } catch (InterruptedException e) { e.printStackTrace(); } } }
构造方法中的状态:RUNNABLE main方法中的状态1:NEW run方法中的状态:RUNNABLE main方法中的状态2:TERMINATED
2.验证TIMED_WAITING
线程状态TIMED_WAITING代表线程执行了Thread.sleep()方法,呈等待状态,等待时间到达,继续向下运行。
示例:从输出结果可以看出,t线程执行了Thread.sleep()方法后,呈TIMED_WAITING状态。
package extthread; public class MyThread extends Thread { @Override public void run() { try { System.out.println("begin sleep"); Thread.sleep(10000); System.out.println(" end sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package test; import extthread.MyThread; public class Run { // NEW, // RUNNABLE, // TERMINATED, // BLOCKED, // WAITING, // TIMED_WAITING, public static void main(String[] args) { try { MyThread t = new MyThread(); t.start(); Thread.sleep(1000); System.out.println("main方法中的状态:" + t.getState()); } catch (InterruptedException e) { e.printStackTrace(); } } }
begin sleep main方法中的状态:TIMED_WAITING end sleep
3.验证BLOCKED
BLOCKED状态出现在某一个线程在等待锁的时候。
示例:b线程等待a线程执行到执行完这段时间处于BLOCKED状态。
package service; public class MyService { synchronized static public void serviceMethod() { try { System.out.println(Thread.currentThread().getName() + "进入了业务方法!"); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
package extthread; import service.MyService; public class MyThread1 extends Thread { @Override public void run() { MyService.serviceMethod(); } }
package extthread; import service.MyService; public class MyThread2 extends Thread { @Override public void run() { MyService.serviceMethod(); } }
package test; import extthread.MyThread1; import extthread.MyThread2; public class Run { // NEW, // RUNNABLE, // TERMINATED, // BLOCKED, // WAITING, // TIMED_WAITING, public static void main(String[] args) { MyThread1 t1 = new MyThread1();
t1.setName("a");
MyThread2 t2 = new MyThread2();
t2.setName("b");
t1.start();
Thread.sleep(100);
t2.start(); System.out.println("main方法中的t2状态:" + t2.getState()); } }
a进入了业务方法! main方法中的t2状态:BLOCKED b进入了业务方法!
4.验证WAITING
WAITING状态是线程执行了Object.wait()方法后所处的状态。
示例:执行完Lock.lock.wait();后线程t进入了WAITING状态。
package service; public class Lock { public static final Byte lock = new Byte("0"); }
package extthread; import service.Lock; public class MyThread extends Thread { @Override public void run() { try { synchronized (Lock.lock) { Lock.lock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package test; import extthread.MyThread; public class Run { // NEW, // RUNNABLE, // TERMINATED, // BLOCKED, // WAITING, // TIMED_WAITING, public static void main(String[] args) { try { MyThread t = new MyThread(); t.start(); Thread.sleep(1000); System.out.println("main方法中的t状态:" + t.getState()); } catch (InterruptedException e) { e.printStackTrace(); } } }
main方法中的t状态:WAITING
二、线程组
线程组的作用是,可以批量地管理线程或线程对象,有效地对线程或线程组对象进行组织。
1.线程对象关联线程组:1级关联
1级关联就是父对象中有子对象,但并不创建子孙对象。这种情况经常出现在开发中,比如创建一些线程时,为了有效地对这些线程进行组织管理,通常的情况下是创建一个线程组,然后再将部分线程归属到该组中。这样的处理可以对零散的线程对象进行有效地组织和规划。
示例:Thread aThread = new Thread(group, aRunnable);Thread bThread = new Thread(group, bRunnable);将线程A和B加入到线程组中,然后分别start启动。
package extthread; public class ThreadA extends Thread { @Override public void run() { try { while (!Thread.currentThread().isInterrupted()) { System.out.println("ThreadName=" + Thread.currentThread().getName()); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package extthread; public class ThreadB extends Thread { @Override public void run() { try { while (!Thread.currentThread().isInterrupted()) { System.out.println("ThreadName=" + Thread.currentThread().getName()); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } } }
活动的线程数为:2 线程组的名称为:group线程组 ThreadName=Thread-2 ThreadName=Thread-3 ThreadName=Thread-2 ThreadName=Thread-3 ThreadName=Thread-3 ThreadName=Thread-2 ...
2.线程对象关联线程组:多级关联
多级关联就是父对象中有子对象,子对象中再创建子对象,也就是出现子孙对象的效果了。但是,线程树结构设计得非常复杂反而不利于线程对象的管理。
示例:首先在main组中添加了一个线程组A,然后在A组中添加了线程对象Z,也就是子孙对象的效果。
package test.run; public class Run { public static void main(String[] args) { // 在main组中添加一个线程组A,然后在这个A组中添加线程对象Z ThreadGroup mainGroup = Thread.currentThread().getThreadGroup(); ThreadGroup group = new ThreadGroup(mainGroup, "A"); Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println("runMethod!"); Thread.sleep(10000);//线程必须在运行状态才可以受组管理 } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread newThread = new Thread(group, runnable); newThread.setName("Z"); newThread.start();// 线程必须启动后才归到A组中 // ///
//方法activeGroupCount()的作用是取得当前线程组对象中的子线程组数量
//方法enumerate()的作用是将线程组中的子线程组以复制的形式拷贝到ThreadGroup[]数组对象中 ThreadGroup[] listGroup = new ThreadGroup[Thread.currentThread() .getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(listGroup); System.out.println("main线程中子线程组个数:" + listGroup.length + " 名称为:" + listGroup[0].getName()); Thread[] listThread = new Thread[listGroup[0].activeCount()]; listGroup[0].enumerate(listThread); System.out.println(listThread[0].getName()); } }
runMethod! main线程中子线程组个数:1 名称为:A Z
3.线程组自动归属特性
自动归属就是自动归到当前线程中。
示例:在实例化一个ThreadGroup线程组x时如果不指定所属的线程组,则x线程组自动归到当前线程对象所属的线程组中,也就是隐式地在一个线程组中添加了一个子线程组,所以在控制台中打印的线程组数量值由0变成了1。
package test.run; public class Run { public static void main(String[] args) { System.out.println(" "+Thread.currentThread().getName() +" "+Thread.currentThread().getThreadGroup().getName() +" "+Thread.currentThread().getThreadGroup().activeGroupCount()); ThreadGroup group=new ThreadGroup("新建线程组"); System.out.println(" "+Thread.currentThread().getName() +" "+Thread.currentThread().getThreadGroup().getName() +" "+Thread.currentThread().getThreadGroup().activeGroupCount()); ThreadGroup[] threadGroup=new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(threadGroup); for (int i = 0; i < threadGroup.length; i++) { System.out.println("第一个线程组名称为:"+threadGroup[i].getName()); } } }
main main 0 main main 1 第一个线程组名称为:新建线程组
4.获取根线程组
示例:main线程所在的线程组为main线程组,JVM的根线程组就是system,再去其父线程则出现了空指针异常。
package test.run; public class Run { public static void main(String[] args) { System.out.println("确定当前线程是:" + Thread.currentThread().getName() + " 所在线程组名称为:"+ Thread.currentThread().getThreadGroup().getName()); System.out.println("main所在的线程组的父线程的名称为:" + Thread.currentThread().getThreadGroup().getParent().getName()); System.out.println("main所在的线程组的父线程的父线程的名称为:" + Thread.currentThread().getThreadGroup().getParent() .getParent().getName()); } }
确定当前线程是:main 所在线程组名称为:main main所在的线程组的父线程的名称为:system Exception in thread "main" java.lang.NullPointerException at test.run.Run.main(Run.java:12)
5.线程组里加线程组
示例:使用ThreadGroup newGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "newGroup");显示地在一个线程组中添加了一个子线程组。
package test.run; public class Run { public static void main(String[] args) { System.out.println("1当前线程组名称: "+ Thread.currentThread().getThreadGroup().getName()); System.out.println("2当前线程组中活动的线程数量: "+ Thread.currentThread().getThreadGroup().activeCount()); System.out.println("3添加之前线程组中线程组的数量: "+ Thread.currentThread().getThreadGroup().activeGroupCount()); ThreadGroup newGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "newGroup"); System.out.println("4添加之前线程组中线程组的数量: "+ Thread.currentThread().getThreadGroup().activeGroupCount()); System.out.println("5父线程组名称: "+ Thread.currentThread().getThreadGroup().getParent().getName()); }
1当前线程组名称: main 2当前线程组中活动的线程数量: 1 3添加之前线程组中线程组的数量: 0 4添加之前线程组中线程组的数量: 1 5父线程组名称: system
6.组内的线程批量停止
示例:调用group.interrupt();使5个线程全部都从无限死循环中中断,可以将正在运行的线程批量停止。
package mythread; public class MyThread extends Thread { public MyThread(ThreadGroup group, String name) { super(group, name); } @Override public void run() { System.out.println("ThreadName=" + Thread.currentThread().getName()+ "进入循环前)"); while (!this.isInterrupted()) { } System.out.println("ThreadName=" + Thread.currentThread().getName()+ "进入循环后)"); } }
package test.run; import mythread.MyThread; public class Run { public static void main(String[] args) { try { ThreadGroup group = new ThreadGroup("线程组"); for (int i = 0; i < 5; i++) { MyThread thread = new MyThread(group, "线程" + (i + 1)); thread.start(); } Thread.sleep(5000); group.interrupt(); System.out.println("调用了group.interrupt();"); } catch (InterruptedException e) { System.out.println("进入catch代码块!"); e.printStackTrace(); } } }
ThreadName=线程1进入循环前! ThreadName=线程5进入循环前! ThreadName=线程4进入循环前! ThreadName=线程2进入循环前! ThreadName=线程3进入循环前! 调用了group.interrupt(); ThreadName=线程4进入循环后! ThreadName=线程3进入循环后! ThreadName=线程2进入循环后! ThreadName=线程1进入循环后! ThreadName=线程5进入循环后!
7.递归与非递归取得组内对象
示例:Thread.currentThread().getThreadGroup().enumerate(listGroup1, true);线程A的参数设置为true是递归取得子组及子孙组,而Thread.currentThread().getThreadGroup().enumerate(listGroup2, false);线程B的参数设置为false是非递归取得线程组内的对象。
package test.run; public class Run { public static void main(String[] args) { ThreadGroup mainGroup = Thread.currentThread().getThreadGroup(); ThreadGroup groupA = new ThreadGroup(mainGroup, "A"); Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println("runMethod!"); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }; ThreadGroup groupB = new ThreadGroup(groupA, "B"); ThreadGroup[] listGroup1 = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(listGroup1, true); for (int i = 0; i < listGroup1.length; i++) { if (listGroup1[i] != null) { System.out.println(listGroup1[i].getName()); } } ThreadGroup[] listGroup2 = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(listGroup2, false); for (int i = 0; i < listGroup2.length; i++) { if (listGroup2[i] != null) { System.out.println(listGroup2[i].getName()); } } } }
A B A
三、使线程具有有序性
正常的情况下,线程在运行时多个线程之间执行任务的时机是无序的,可以通过改造代码的方式使它们运行具有有序性。
示例:通过给不同线程的showNumPosition赋予不同的值以及if (addNumber % 3 == showNumPosition) {...}判断语句来实现代码有序运行的效果。
package mythread; public class MyThread extends Thread { private Object lock; private String showChar; private int showNumPosition; private int printCount = 0;//统计打印了几个字母 volatile private static int addNumber = 1; public MyThread(Object lock, String showChar, int showNumPosition) { super(); this.lock = lock; this.showChar = showChar; this.showNumPosition = showNumPosition; } @Override public void run() { try { synchronized (lock) { while (true) { if (addNumber % 3 == showNumPosition) { System.out.println("ThreadName="+ Thread.currentThread().getName() + " runCount=" + addNumber + " " + showChar); lock.notifyAll(); addNumber++; printCount++; if (printCount == 3) { break; } } else { lock.wait(); } } } } catch (InterruptedException e) { e.printStackTrace(); } } }
package mythread; public class MyThread extends Thread { private Object lock; private String showChar; private int showNumPosition; private int printCount = 0;//统计打印了几个字母 volatile private static int addNumber = 1; public MyThread(Object lock, String showChar, int showNumPosition) { super(); this.lock = lock; this.showChar = showChar; this.showNumPosition = showNumPosition; } @Override public void run() { try { synchronized (lock) { while (true) { if (addNumber % 3 == showNumPosition) { System.out.println("ThreadName="+ Thread.currentThread().getName() + " runCount=" + addNumber + " " + showChar); lock.notifyAll(); addNumber++; printCount++; if (printCount == 3) { break; } } else { lock.wait(); } } } } catch (InterruptedException e) { e.printStackTrace(); } } }
ThreadName=Thread-0 runCount=1 A ThreadName=Thread-1 runCount=2 B ThreadName=Thread-2 runCount=3 C ThreadName=Thread-0 runCount=4 A ThreadName=Thread-1 runCount=5 B ThreadName=Thread-2 runCount=6 C ThreadName=Thread-0 runCount=7 A ThreadName=Thread-1 runCount=8 B ThreadName=Thread-2 runCount=9 C
四、SimpleDateFormat非线程安全
类SimpleDateFormat主要负责日期的转换与格式化,但在多线程的环境中,使用此类容易造成数据转换及处理的不准确,因为SimpleDateFormat类并不是线程安全的。
1.出现异常
示例:使用单例的SimpleDateFormat类在多线程的环境中处理日期,极易出现日期转换错误的情况。
package extthread; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread extends Thread { private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = sdf.parse(dateString); String newDateString = sdf.format(dateRef).toString(); if (!newDateString.equals(dateString)) { System.out.println("ThreadName=" + this.getName() + "日期字符串为:" + dateString + " 转换成的日期为:" + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
package test.run; import java.text.SimpleDateFormat; import extthread.MyThread; public class Test { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[] { "2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05", "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10" }; MyThread[] threadArray = new MyThread[10]; for (int i = 0; i < 10; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < 10; i++) { threadArray[i].start(); } } }
ThreadName=Thread-5日期字符串为:2000-01-06 转换成的日期为:2000-01-04 Exception in thread "Thread-1" ThreadName=Thread-2日期字符串为:2000-01-03 转换成的日期为:2200-01-03 ThreadName=Thread-4日期字符串为:2000-01-05 转换成的日期为:2000-01-04 ThreadName=Thread-0日期字符串为:2000-01-01 转换成的日期为:2200-01-01 java.lang.NumberFormatException: empty String at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source) at sun.misc.FloatingDecimal.parseDouble(Unknown Source) at java.lang.Double.parseDouble(Unknown Source) at java.text.DigitList.getDouble(Unknown Source) at java.text.DecimalFormat.parse(Unknown Source) at java.text.SimpleDateFormat.subParse(Unknown Source) at java.text.SimpleDateFormat.parse(Unknown Source) at java.text.DateFormat.parse(Unknown Source) at extthread.MyThread.run(MyThread.java:21) ThreadName=Thread-7日期字符串为:2000-01-08 转换成的日期为:2000-01-09
2.解决异常方法1
示例:可以通过创建多个SimpleDateFormat类的实例来解决异常问题,由于 if (!newDateString.equals(dateString)) 判断语句判断为假,所以不会执行输出语句,控制台没有输出,原因就是每个线程单独处理自己的SimpleDateFormat类的实例,互相互不干扰。
package tools; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateTools { public static Date parse(String formatPattern, String dateString) throws ParseException { return new SimpleDateFormat(formatPattern).parse(dateString); } public static String format(String formatPattern, Date date) { return new SimpleDateFormat(formatPattern).format(date).toString(); } }
package extthread; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import tools.DateTools; public class MyThread extends Thread { private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = DateTools.parse("yyyy-MM-dd", dateString); String newDateString = DateTools.format("yyyy-MM-dd", dateRef) .toString(); if (!newDateString.equals(dateString)) { System.out.println("ThreadName=" + this.getName() + "日子字符串:" + dateString + "转换成的日期为:" + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
package test.run; import java.text.SimpleDateFormat; import extthread.MyThread; public class Test { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[] { "2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05", "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10" }; MyThread[] threadArray = new MyThread[10]; for (int i = 0; i < 10; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < 10; i++) { threadArray[i].start(); } } }
3.解决异常方法2
示例:通过使用ThreadLocal类能使线程绑定到指定的对象的特性,也可以解决多线程环境下SimpleDateFormat类处理错误的情况。使用tl.set(sdf);代码使线程和SimpleDateFormat类的实例对象绑定,也可以达到线程之间互相处理不受干扰的效果。
package tools; import java.text.SimpleDateFormat; public class DateTools { private static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(); public static SimpleDateFormat getSimpleDateFormat(String datePattern) { SimpleDateFormat sdf = null; sdf = tl.get(); if (sdf == null) { sdf = new SimpleDateFormat(datePattern); tl.set(sdf); } return sdf; } }
package extthread; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import tools.DateTools; public class MyThread extends Thread { private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse( dateString); String newDateString = DateTools.getSimpleDateFormat("yyyy-MM-dd") .format(dateRef).toString(); if (!newDateString.equals(dateString)) { System.out.println("ThreadName=" + this.getName() + "日期字符串:" + dateString + " 转换之后的日期:" + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
五、线程中出现异常的处理
package extthread; public class MyThread extends Thread { @Override public void run() { String username = null; System.out.println(username.hashCode()); } }
示例1:由于username字段为null,所以运行后输出了空指针异常。
package controller; import extthread.MyThread; public class Main1 { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }
Exception in thread "Thread-0" java.lang.NullPointerException at extthread.MyThread.run(MyThread.java:7)
示例2:在Java的多线程技术中,可以使用UncaughtExceptionHandler类对多线程中的异常进行“捕捉”,t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){}的作用是对t1线程对象设置默认的异常处理器。
package controller; import java.lang.Thread.UncaughtExceptionHandler; import extthread.MyThread; public class Main2 { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.setName("线程t1"); t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程:" + t.getName() + " 出现了异常"); e.printStackTrace(); } }); t1.start(); MyThread t2 = new MyThread(); t2.setName("线程t2"); t2.start(); } }
Exception in thread "线程t2" 线程:线程t1 出现了异常 java.lang.NullPointerException at extthread.MyThread.run(MyThread.java:7) java.lang.NullPointerException at extthread.MyThread.run(MyThread.java:7)
示例3:还可以使用MyThread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {}对所有线程对象设置异常处理器。
package controller; import java.lang.Thread.UncaughtExceptionHandler; import extthread.MyThread; public class Main3 { public static void main(String[] args) { MyThread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程:" + t.getName() + " 出现了异常!"); e.printStackTrace(); } }); MyThread t1 = new MyThread(); t1.setName("线程t1"); t1.start(); MyThread t2 = new MyThread(); t2.setName("线程t2"); t2.start(); } }
线程:线程t1 出现了异常! 线程:线程t2 出现了异常! java.lang.NullPointerException at extthread.MyThread.run(MyThread.java:7) java.lang.NullPointerException at extthread.MyThread.run(MyThread.java:7)
六、线程组内处理异常
示例1:程序运行后,其中一个线程出现异常,但是其他线程却一直以死循环的方式持续打印结果。默认情况下,线程组中一个线程出现异常不会影响到其他线程的运行。
package extthread; public class MyThread extends Thread { private String num; public MyThread(ThreadGroup group, String name, String num) { super(group, name); this.num = num; } @Override public void run() { int numInt = Integer.parseInt(num); while (true) { System.out.println("死循环中:" + Thread.currentThread().getName()); } } }
package test.run; import extthread.MyThread; public class Run { public static void main(String[] args) { ThreadGroup group = new ThreadGroup("我的线程组"); MyThread[] myThread = new MyThread[10]; for (int i = 0; i < myThread.length; i++) { myThread[i] = new MyThread(group, "线程" + (i + 1), "1"); myThread[i].start(); } MyThread newT = new MyThread(group, "报错线程", "a"); newT.start(); } }
死循环中:线程6Exception in thread "报错线程" java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source)死循环中:线程9 at java.lang.Integer.parseInt(Unknown Source) ...
示例2:要想实现线程组内一个线程出现异常后全部线程都停止运行的话,使用public void uncaughtException(Thread t, Throwable e) {...}方法,其中t参数是出现异常的线程对象,这种情况下需要注意的就是,每个线程的run(0方法内部不要有异常catch语句,如果有catch语句,那么uncaughtException方法就不会执行。通过输出也可以看出,其中一个线程出现了异常,其他线程全部停止了。
package extthreadgroup; public class MyThreadGroup extends ThreadGroup { public MyThreadGroup(String name) { super(name); } @Override public void uncaughtException(Thread t, Throwable e) { super.uncaughtException(t, e); this.interrupt(); } }
package extthread; public class MyThread extends Thread { private String num; public MyThread(ThreadGroup group, String name, String num) { super(group, name); this.num = num; } @Override public void run() { int numInt = Integer.parseInt(num); while (this.isInterrupted() == false) { System.out.println("死循环中:" + Thread.currentThread().getName()); } } }
package test.run; import extthread.MyThread; import extthreadgroup.MyThreadGroup; public class Run { public static void main(String[] args) { MyThreadGroup group = new MyThreadGroup("我的线程组"); MyThread[] myThread = new MyThread[10]; for (int i = 0; i < myThread.length; i++) { myThread[i] = new MyThread(group, "线程" + (i + 1), "1"); myThread[i].start(); } MyThread newT = new MyThread(group, "报错线程", "a"); newT.start(); } }
... 死循环中:线程3 死循环中:线程3 死循环中:线程3 at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) 死循环中:线程3 at extthread.MyThread.run(MyThread.java:14) 死循环中:线程3 死循环中:线程3 死循环中:线程3 死循环中:线程3 死循环中:线程3 死循环中:线程3 死循环中:线程3 死循环中:线程7 死循环中:线程6 死循环中:线程9 死循环中:线程2 死循环中:线程10 死循环中:线程4 死循环中:线程1 死循环中:线程8 死循环中:线程5
七、线程异常处理的传递
这么多异常处理的方式,如果放在一起运行,出现的结果会不一样。
package extthread; public class MyThread extends Thread { private String num = "a"; public MyThread() { super(); } public MyThread(ThreadGroup group, String name) { super(group, name); } @Override public void run() { int numInt = Integer.parseInt(num); System.out.println("在线程中打印:" + (numInt + 1)); } }
package extthreadgroup; public class MyThreadGroup extends ThreadGroup { public MyThreadGroup(String name) { super(name); } @Override public void uncaughtException(Thread t, Throwable e) { super.uncaughtException(t, e); System.out.println("线程组的异常处理"); e.printStackTrace(); } }
package test.extUncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler; public class ObjectUncaughtExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("对象的异常处理"); e.printStackTrace(); } }
package test.extUncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler; public class StateUncaughtExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("静态的异常处理"); e.printStackTrace(); } }
示例1:这种情况下,对象的异常处理被运行。
package test; import test.extUncaughtExceptionHandler.ObjectUncaughtExceptionHandler; import test.extUncaughtExceptionHandler.StateUncaughtExceptionHandler; import extthread.MyThread; public class Run1 { public static void main(String[] args) { MyThread myThread = new MyThread(); // 对象 myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); // 类 MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler()); myThread.start(); } }
对象的异常处理 java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17)
示例2:对对象添加注释后,则是静态的异常处理被执行。
package test; import test.extUncaughtExceptionHandler.ObjectUncaughtExceptionHandler; import test.extUncaughtExceptionHandler.StateUncaughtExceptionHandler; import extthread.MyThread; public class Run1 { public static void main(String[] args) { MyThread myThread = new MyThread(); // 对象 // myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); // 类 MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler()); myThread.start(); } }
静态的异常处理 java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17)
示例3:这种情况下,对象的异常处理被运行。
package test; import test.extUncaughtExceptionHandler.ObjectUncaughtExceptionHandler; import test.extUncaughtExceptionHandler.StateUncaughtExceptionHandler; import extthread.MyThread; import extthreadgroup.MyThreadGroup; public class Run2 { public static void main(String[] args) { MyThreadGroup group = new MyThreadGroup("我的线程组"); MyThread myThread = new MyThread(group, "我的线程"); // 对象 myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); // 类 MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler()); myThread.start(); } }
对象的异常处理 java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17)
示例4:这种情况下,静态的异常处理和线程组的异常处理都被执行。(打印顺序可能不一样。)其中要想打印“静态的异常处理”具体信息,必须在public void uncaughtException(Thread t, Throwable e) {...}方法中加上super.uncaughtException(t, e);代码才行。
package test; import test.extUncaughtExceptionHandler.ObjectUncaughtExceptionHandler; import test.extUncaughtExceptionHandler.StateUncaughtExceptionHandler; import extthread.MyThread; import extthreadgroup.MyThreadGroup; public class Run2 { public static void main(String[] args) { MyThreadGroup group = new MyThreadGroup("我的线程组"); MyThread myThread = new MyThread(group, "我的线程"); // 对象 // myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); // 类 MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler()); myThread.start(); } }
静态的异常处理 线程组的异常处理 java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17) java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17)
示例5:两部分都注释后,这种情况下,线程组的异常处理被执行。
package test; import test.extUncaughtExceptionHandler.ObjectUncaughtExceptionHandler; import test.extUncaughtExceptionHandler.StateUncaughtExceptionHandler; import extthread.MyThread; import extthreadgroup.MyThreadGroup; public class Run2 { public static void main(String[] args) { MyThreadGroup group = new MyThreadGroup("我的线程组"); MyThread myThread = new MyThread(group, "我的线程"); // 对象 // myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); // 类 // MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler()); myThread.start(); } }
Exception in thread "我的线程" java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17) 线程组的异常处理 java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at extthread.MyThread.run(MyThread.java:17)