一、使线程具有有序性
正常的情况下线程运行是无序的,但是可以通过改造代码的方式使他们运行具有有序性。
package com.it.po.thread14; public class MyThread7 extends Thread { private Object lock; private String showChar; private int showNumPosition; private int printCount; volatile private static int addNumber=1; public MyThread7(Object lock, String showChar, int showNumPosition) { 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("线程 "+Thread.currentThread().getName() +" runCount "+addNumber+" "+showChar); lock.notifyAll(); addNumber++; printCount++; if(printCount==3){//只有三条线程 break; } }else {//不是的先等着 lock.wait(); } } } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread14; import com.it.po.thread05.Task1; public class Run14 { public static void main(String[] args) throws InterruptedException { Object lock = new Object(); MyThread7 t1 = new MyThread7(lock,"A",1); MyThread7 t2 = new MyThread7(lock,"B",2); MyThread7 t3 = new MyThread7(lock,"C",0); t1.start(); t2.start(); t3.start(); } }
线程 Thread-0 runCount 1 A 线程 Thread-1 runCount 2 B 线程 Thread-2 runCount 3 C 线程 Thread-0 runCount 4 A 线程 Thread-1 runCount 5 B 线程 Thread-2 runCount 6 C 线程 Thread-0 runCount 7 A 线程 Thread-1 runCount 8 B 线程 Thread-2 runCount 9 C
二、SimpleDateFormat 非线程安全
类SimpleDateFormat主要负责日期的转化与格式化,但是在多线程环境
使用此类易造成数据转化及处理的不正确,因为SimpleDateFormat并不是线程安全。
(一)出现的异常
package com.it.po.thread14; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread8 extends Thread { private String dateString; private SimpleDateFormat simpleDateFormat; public MyThread8(String dateString, SimpleDateFormat simpleDateFormat) { this.dateString = dateString; this.simpleDateFormat = simpleDateFormat; } @Override public void run() { try { Date parse = simpleDateFormat.parse(dateString); String newDateString = simpleDateFormat.format(parse).toString(); if(!dateString.equals(newDateString)){ System.out.println("线程 "+this.getName()+" 报错了 日期字符串 "+dateString +" 转化成日期 "+newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
package com.it.po.thread14; import java.text.SimpleDateFormat; public class Run12 { public static void main(String[] args) throws InterruptedException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String[] dataStringArray = new String[]{"2019-12-01","2019-12-02", "2019-12-03","2019-12-04","2019-12-05","2019-12-06", "2019-12-07","2019-12-08","2019-12-09","2019-12-10",}; MyThread8[] thread8s = new MyThread8[10]; for(int i=0;i<10;i++){ thread8s[i]=new MyThread8(dataStringArray[i],format); } for(int i=0;i<10;i++){ thread8s[i].start(); } } }
线程 Thread-0 报错了 日期字符串 2019-12-01 转化成日期 2019-12-05 线程 Thread-3 报错了 日期字符串 2019-12-04 转化成日期 2019-12-05 线程 Thread-2 报错了 日期字符串 2019-12-03 转化成日期 2019-12-05 线程 Thread-7 报错了 日期字符串 2019-12-08 转化成日期 2019-12-09 线程 Thread-1 报错了 日期字符串 2019-12-02 转化成日期 0001-12-10
(二)解决方案一
package com.it.po.thread14; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateTools { public static Date pare(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); } }
package com.it.po.thread14; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread9 extends Thread { private String dateString; private SimpleDateFormat simpleDateFormat; public MyThread9(String dateString, SimpleDateFormat simpleDateFormat) { this.dateString = dateString; this.simpleDateFormat = simpleDateFormat; } @Override public void run() { try { Date date = DateTools.pare("yyyy-MM-dd",dateString); String newDateString = DateTools.format("yyyy-MM-dd",date); if(!dateString.equals(newDateString)){ System.out.println("线程 "+this.getName()+" 报错了 日期字符串 "+dateString +" 转化成日期 "+newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
package com.it.po.thread14; import java.text.SimpleDateFormat; public class Run12 { public static void main(String[] args) throws InterruptedException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String[] dataStringArray = new String[]{"2019-12-01","2019-12-02", "2019-12-03","2019-12-04","2019-12-05","2019-12-06", "2019-12-07","2019-12-08","2019-12-09","2019-12-10",}; MyThread9[] threads = new MyThread9[10]; for(int i=0;i<10;i++){ threads[i]=new MyThread9(dataStringArray[i],format); } for(int i=0;i<10;i++){ threads[i].start(); } } }

无打印。
原理就是创建了多个SimpleDateFormat对象。
(三)、解决方案二
ThreadLock类能使线程绑定到指定的对象中,使用该类也可以
解决目前的问题
package com.it.po.thread14; import java.text.SimpleDateFormat; public class DateTools2 { private static ThreadLocal<SimpleDateFormat> t1 =new ThreadLocal<SimpleDateFormat>(); public static SimpleDateFormat getSimpleDateFormat( String datePattern) { SimpleDateFormat simpleDateFormat =null; simpleDateFormat=t1.get(); if(simpleDateFormat==null){ simpleDateFormat = new SimpleDateFormat(datePattern); t1.set(simpleDateFormat); } return simpleDateFormat; } }
package com.it.po.thread14; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread10 extends Thread { private String dateString; private SimpleDateFormat simpleDateFormat; public MyThread10(String dateString, SimpleDateFormat simpleDateFormat) { this.dateString = dateString; this.simpleDateFormat = simpleDateFormat; } @Override public void run() { try { Date date = DateTools2.getSimpleDateFormat("yyyy-MM-dd").parse(dateString); String newDateString = DateTools2.getSimpleDateFormat("yyyy-MM-dd").format(date).toString(); if(!dateString.equals(newDateString)){ System.out.println("线程 "+this.getName()+" 报错了 日期字符串 "+dateString +" 转化成日期 "+newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }
package com.it.po.thread14; import java.text.SimpleDateFormat; public class Run12 { public static void main(String[] args) throws InterruptedException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String[] dataStringArray = new String[]{"2019-12-01","2019-12-02", "2019-12-03","2019-12-04","2019-12-05","2019-12-06", "2019-12-07","2019-12-08","2019-12-09","2019-12-10",}; MyThread10[] threads = new MyThread10[10]; for(int i=0;i<10;i++){ threads[i]=new MyThread10(dataStringArray[i],format); } for(int i=0;i<10;i++){ threads[i].start(); } } }

三、线程中出现异常的处理
package com.it.po.thread14; public class MyThread11 extends Thread { @Override public void run() { super.run(); String username=null; System.out.println("username = "+username.hashCode()); } }
package com.it.po.thread14; public class Run13 { public static void main(String[] args) throws InterruptedException { MyThread11 t1 = new MyThread11(); t1.start(); } }
如何处理线程异常呢?
在多线程中使用UncaughtExceptionHandler 类从而对发生的异常进行有效的控制。
package com.it.po.thread14; public class Run13 { public static void main(String[] args) throws InterruptedException { MyThread11 t1 = new MyThread11(); t1.setName(" 线程t1 "); t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程 "+t.getName() + " 出现异常了: "); e.printStackTrace(); } }); t1.start(); MyThread11 t2 = new MyThread11(); t2.setName(" 线程t2 "); t2.start(); } }
Exception in thread " 线程t2 " java.lang.NullPointerException at com.it.po.thread14.MyThread11.run(MyThread11.java:8) java.lang.NullPointerException at com.it.po.thread14.MyThread11.run(MyThread11.java:8) 线程 线程t1 出现异常了:
setUncaughtExceptionHandler 方法对指定的线程对象设置默认的异常处理器。
在Thread类中还可以使用setDefaultUncaughtExceptionHandler 对所有线程对象设置异常
处理器。
package com.it.po.thread14; public class Run15 { public static void main(String[] args) throws InterruptedException { MyThread11.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程 "+t.getName() + " 出现异常了: "); e.printStackTrace(); } }); MyThread11 t1 = new MyThread11(); t1.setName(" 线程t1 "); t1.start(); MyThread11 t2 = new MyThread11(); t2.setName(" 线程t2 "); t2.start(); } }
线程 线程t1 出现异常了: 线程 线程t2 出现异常了: java.lang.NullPointerException at com.it.po.thread14.MyThread11.run(MyThread11.java:8) java.lang.NullPointerException at com.it.po.thread14.MyThread11.run(MyThread11.java:8)
四、线程组内处理异常
package com.it.po.thread14; public class MyThread12 extends Thread { private String num; public MyThread12(ThreadGroup group,String name ,String num) { super(group,name); this.num = num; } @Override public void run() { super.run(); int newNum = Integer.parseInt(num); while (true){ System.out.println("线程死循环中 "+Thread.currentThread().getName()); } } }
package com.it.po.thread14; public class Run16 { public static void main(String[] args) throws InterruptedException { ThreadGroup group = new ThreadGroup("我的线程组"); MyThread12[] thread12s = new MyThread12[10]; for(int i=0;i<10;i++){ thread12s[i]=new MyThread12(group,"线程 "+(i+1),"1"); thread12s[i].start(); } MyThread12 myThread12 = new MyThread12(group, "报错线程 ", "a"); myThread12.start(); } }
运行的时候虽然报错的线程 出现异常了,但是其他线程由于是while循环中
还会继续运行,像这样,线程组内一个线程出现异常后全部线程都停止,该如何呢?
package com.it.po.thread14; 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 com.it.po.thread14; public class MyThread13 extends Thread { private String num; public MyThread13(ThreadGroup group, String name , String num) { super(group,name); this.num = num; } @Override public void run() { super.run(); int newNum = Integer.parseInt(num); while (this.isInterrupted()==false){ System.out.println("线程死循环中 "+Thread.currentThread().getName()); } } }
package com.it.po.thread14; public class Run17 { public static void main(String[] args) throws InterruptedException { MyThreadGroup group = new MyThreadGroup("我的线程组"); MyThread13[] thread12s = new MyThread13[10]; for(int i=0;i<10;i++){ thread12s[i]=new MyThread13(group,"线程 "+(i+1),"1"); thread12s[i].start(); } MyThread13 myThread12 = new MyThread13(group, "报错线程 ", "a"); myThread12.start(); } }

注意:使用自定义的java.lang.ThreadGroup线程组,并且重写 uncaughtException
方法处理组内线程中断行为时,每一个线程对象中的run(),方法内不要有异常catch语句。
否则, public void uncaughtException(Thread t, Throwable e) 方法不执行。
五、线程异常处理的传递。
前面介绍了异常处理的方式,那么这些处理的方式如果放在一起运行,会怎么样?
package com.it.po.thread14; public class MyThread14 extends Thread { private String num="a"; public MyThread14() { } public MyThread14(ThreadGroup group,String name) { super(group,name); } @Override public void run() { super.run(); int a = Integer.parseInt(num); System.out.println("在线程中打印。。。"+(a+1)); } }
package com.it.po.thread14; public class MyThreadGroup14 extends ThreadGroup { public MyThreadGroup14(String name) { super(name); } @Override public void uncaughtException(Thread t, Throwable e) { super.uncaughtException(t, e); System.out.println("线程组的异常处理。。"); e.printStackTrace(); } }
package com.it.po.thread14; public class ObjectUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("对象异常处理。。"); e.printStackTrace(); } }
package com.it.po.thread14; public class StaticUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("静态异常处理。。"); e.printStackTrace(); } }
package com.it.po.thread14; public class Run18 { public static void main(String[] args) throws InterruptedException { MyThread14 t = new MyThread14(); t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(new StaticUncaughtExceptionHandler()); t.start(); } }

对象异常运行
修改一
package com.it.po.thread14; public class Run18 { public static void main(String[] args) throws InterruptedException { MyThread14 t = new MyThread14(); // t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(new StaticUncaughtExceptionHandler()); t.start(); } }

修改二
package com.it.po.thread14; public class Run18 { public static void main(String[] args) throws InterruptedException { MyThreadGroup14 myThreadGroup14 = new MyThreadGroup14("我的线程组"); MyThread14 t = new MyThread14(myThreadGroup14,"我的线程"); t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(new StaticUncaughtExceptionHandler()); t.start(); } }

修改三
package com.it.po.thread14; public class Run18 { public static void main(String[] args) throws InterruptedException { MyThreadGroup14 myThreadGroup14 = new MyThreadGroup14("我的线程组"); MyThread14 t = new MyThread14(myThreadGroup14,"我的线程"); // t.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(new StaticUncaughtExceptionHandler()); t.start(); } }

浙公网安备 33010602011771号