• 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)
    
  • 相关阅读:
    POJ3264 线段树模板
    Dijkstra算法模板 C++
    POJ 1287 Prim算法模板
    ZOJ 2107 Quoit Design(分治法解最近对模板题)
    POJ 3714 Raid 分治法求最近对问题
    最长上升子序列(LIS)及其优化O(nlongn)
    PAT1146 Topological Order
    PAT甲级 _A+B Format
    PAT乙级_A+B与C
    PAT乙级_继续3n+1猜想
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8991397.html
Copyright © 2020-2023  润新知