1、线程的状态
线程对象在不同的运行时期有不同的状态,状态信息就存在于State枚举类中
Thread.State :
NEW :至今尚未启动的线程
RUNNABLE:正在java虚拟机中执行的线程
BLOCKED:受阻塞于并等待某个监视器锁的线程
WAITING:无限期的等待另一个线程来执行某一个特定操作的线程
TIMED_WAITING:等待另一个线程来执行某一特定操作的线程
TERMINATED:已退出的线程
package com.threadTest.thread.add.state.test01;/** * Created by sky on 2017/3/23. */public class Run { public static void main(String[] args) { try { MyThread thread01 = new MyThread(); System.out.println("main 方法中的状态1: " + thread01.getState()); Thread.sleep(1000); thread01.start(); Thread.sleep(1000); System.out.println("main 方法中的状态2: " + thread01.getState()); MyThread thread02 = new MyThread(); System.out.println("main 方法中的状态1: " + thread02.getState()); Thread.sleep(1000); thread02.start(); Thread.sleep(1000); System.out.println("main 方法中的状态2: " + thread02.getState()); } catch (InterruptedException e) { e.printStackTrace(); } }} 输出: 构造方法中的状态1: RUNNABLE 构造方法中的状态2: RUNNABLE main 方法中的状态1: NEW run 构造方法中的状态1: RUNNABLE main 方法中的状态2: TIMED_WAITING 构造方法中的状态1: RUNNABLE 构造方法中的状态2: RUNNABLE main 方法中的状态1: NEW run 构造方法中的状态1: RUNNABLE main 方法中的状态2: TIMED_WAITING
package com.threadTest.thread.add.state.test01;/** * Created by sky on 2017/3/23. */public class Lock { public static final Byte lock = new Byte("0");}
package com.threadTest.thread.add.state.test01;/** * Created by sky on 2017/3/23. */public class MyThread extends Thread { public MyThread() { try { System.out.println("构造方法中的状态1: " + Thread.currentThread().getState()); Thread.sleep(1000); System.out.println("构造方法中的状态2: " + Thread.currentThread().getState()); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { try { System.out.println("run 构造方法中的状态1: " + Thread.currentThread().getState()); Thread.sleep(1000); try { synchronized (Lock.lock) { Thread.sleep(1000); Lock.lock.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("run 构造方法中的状态2: " + Thread.currentThread().getState()); } catch (InterruptedException e) { e.printStackTrace(); } }}
2、线程组
线程组作用:可以批量的管理线程或线程组对象,有效地对线程或线程组对象进行组织。
1)线程对象关联线程组:1级关联
1级关联:父对象中有子对象,但并不创建子孙对象。为了有效地对这些线程进行组织管理,通常情况下是创建一个线程组,然后将部分线程归属到该组中,对零散的线程进行有限的组织与规划。
package com.threadTest.thread.add.state.group;/** * Created by sky on 2017/3/23. */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 com.threadTest.thread.add.state.group;/** * Created by sky on 2017/3/23. */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(); } }}
package com.threadTest.thread.add.state.group;/** * Created by sky on 2017/3/23. */public class Run { public static void main(String[] args) { ThreadA threadA = new ThreadA(); ThreadB threadB = new ThreadB(); ThreadGroup group = new ThreadGroup("线程组"); Thread athread = new Thread(group, threadA); Thread bthread = new Thread(group, threadB); athread.start(); bthread.start(); System.out.println("interrupt前活动的线程组: " + group.activeCount()); System.out.println("线程组的名称为: " + group.getName()); //线程中断 group.interrupt(); System.out.println("interrupt后活动的线程组: " + group.activeCount()); group.list(); }}输出:interrupt前活动的线程组: 2线程组的名称为: 线程组interrupt后活动的线程组: 2java.lang.ThreadGroup[name=线程组,maxpri=10] Thread[Thread-2,5,线程组] Thread[Thread-3,5,线程组]
2)线程对象关联线程组:多级关联
多级关联:父对象中有子对象,子对象中再创建子对象
package com.threadTest.thread.add.state.group.test02;/** * Created by sky on 2017/3/23. */public class Run { public static void main(String[] args) { //在main组中添加一个线程组A,然后在这个A中添加线程对象Z //方法acctiveGroupCount()个acctiveCount()的值不是固定的 //是系统环境的一个快照 ThreadGroup mainGroup = Thread.currentThread().getThreadGroup(); ThreadGroup group = new ThreadGroup(mainGroup, "A"); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("runMethod!"); try { Thread.sleep(10000); //线程必须在运行状态下才可以受组管理 } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread newThread = new Thread(group, runnable); newThread.setName("Z"); newThread.start();//线程必须启动后才归到A组中 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]线程组中的每个活动线程复制到指定的数组中listThread listGroup[0].enumerate(listThread); System.out.println(listThread[0].getName()); }}
3)线程组自动归属特性
自动归属:自动归到当前线程组中
package com.threadTest.thread.add.state.group.test03;/** * Created by sky on 2017/3/23. */public class Run { public static void main(String[] args) { //方法activeGroupCount()取得当前线程组对象中的子线程数量 //方法acctiveGroupCount()的作用是将线程中的子线程组以复制的形式拷贝到ThreadGroup[]数组对象中 System.out.println("A处线程:" + Thread.currentThread().getName() + "所属的线程组名为:" + Thread.currentThread().getThreadGroup().getName() + " " + "中有线程组数量: " + Thread.currentThread().getThreadGroup().activeGroupCount()); ThreadGroup group = new ThreadGroup("新的组");//自动加到main组中 System.out.println("B处线程:" + Thread.currentThread().getName() + " 所属的线程组名为: " + Thread.currentThread().getThreadGroup().getName() + " 中有线程组数量" + Thread.currentThread().getThreadGroup().activeGroupCount()); ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(threadGroups); for (int i = 0; i < threadGroups.length; i++) { System.out.println("第一个线程组名称为: " + threadGroups[i].getName()); } }} 输出: A处线程:main所属的线程组名为:main 中有线程组数量: 0 B处线程:main 所属的线程组名为: main 中有线程组数量1 第一个线程组名称为: 新的组
4)获取根线程组
package com.threadTest.thread.add.state.group.test04;/** * Created by sky on 2017/3/23. */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()); }} 输出: Exception in thread "main" java.lang.NullPointerException at com.threadTest.thread.add.state.group.test04.Run.main(Run.java:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 线程:main 所在的线程组名为: main at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) main线程所在的线程组的父线程组名称是:system at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
5)线程组里加线程组
package com.threadTest.thread.add.state.group.test05;/** * Created by sky on 2017/3/23. */public class Run { public static void main(String[] args) { System.out.println("线程组名称:" + Thread.currentThread().getThreadGroup().getName()); System.out.println("线程组中活动的线程数量:" + Thread.currentThread().getThreadGroup().activeCount()); System.out.println("线程组中线程组的数量-加之前:" + Thread.currentThread().getThreadGroup().activeGroupCount()); new ThreadGroup(Thread.currentThread().getThreadGroup(), "newGroup"); System.out.println("线程组中线程组数量-加之后:" + Thread.currentThread().getThreadGroup().activeGroupCount()); System.out.println("父线程组名称:" + Thread.currentThread().getThreadGroup().getParent().getName()); }} 输出: 线程组名称:main 线程组中活动的线程数量:2 线程组中线程组的数量-加之前:0 线程组中线程组数量-加之后:1 父线程组名称:system
6)组内的线程的批量停止
package com.threadTest.thread.add.state.group.test06;/** * Created by sky on 2017/3/23. */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 com.threadTest.thread.add.state.group.test06;/** * Created by sky on 2017/3/23. */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("调用了interrupt()方法"); } catch (InterruptedException e) { System.out.println("停了!"); e.printStackTrace(); } }}输出:ThreadName=线程1准备开始死循环了:)ThreadName=线程2准备开始死循环了:)ThreadName=线程3准备开始死循环了:)ThreadName=线程4准备开始死循环了:)ThreadName=线程5准备开始死循环了:)调用了interrupt()方法ThreadName=线程1结束了:)ThreadName=线程5结束了:)ThreadName=线程4结束了:)ThreadName=线程3结束了:)ThreadName=线程2结束了:)
7)递归与非递归取得组内对象
package com.threadTest.thread.add.state.group.test07;import com.threadTest.thread.add.state.group.test06.MyThread;/** * Created by sky on 2017/3/23. */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()]; //传入true是递归取得子组及子孙组 Thread.currentThread().getThreadGroup().enumerate(listGroup1,true); for (int i = 0; i < listGroup1.length; i++) { if (null != listGroup1[i]) { System.out.println(listGroup1[i].getName()); } } ThreadGroup[] listGroup2 = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; //传入true是递归取得子组及子孙组 Thread.currentThread().getThreadGroup().enumerate(listGroup2,false); for (int i = 0; i < listGroup2.length; i++) { if (null != listGroup2[i]) { System.out.println(listGroup2[i].getName()); } } }} 输出: A B A
3、使线程具有有序性
package com.threadTest.thread.add.state.order;/** * Created by sky on 2017/3/24. */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 (showNumPosition == addNumber % 3) { System.out.println("ThreadName=" + Thread.currentThread().getName() + " runCount=" + addNumber + " " + showChar); lock.notifyAll(); addNumber++; printCount++; if (3 == printCount) { 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
4、SimpleDateFormat非线程安全
1)出现异常
package com.threadTest.thread.add.state.simpleDateFormat.test01;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by sky on 2017/3/24. */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 com.threadTest.thread.add.state.simpleDateFormat.test01;import java.text.SimpleDateFormat;/** * Created by sky on 2017/3/24. */public class Test { public static void main(String[] args) { //共享变量SimpleDateFormat 导致 java.lang.NumberFormatException: multiple points SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[]{"2000-01-01", "2001-02-02", "2003-03-03", "2004-04-04"}; MyThread[] threadArray = new MyThread[4]; for (int i = 0; i < dateStringArray.length; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < dateStringArray.length; i++) { threadArray[i].start(); } }}输出:Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ""ThreadName= Thread-0报错了 日期字符串: 2000-01-01 转换成的日期为: 2001-01-01 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)ThreadName= Thread-3报错了 日期字符串: 2004-04-04 转换成的日期为: 2003-03-03
2)解决方法一
package com.threadTest.thread.add.state.simpleDateFormat.test02;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by sky on 2017/3/24. * 实际创建了多个SimpleDateFormat */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 com.threadTest.thread.add.state.simpleDateFormat.test02;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by sky on 2017/3/24. */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); if (!newDateString.equals(dateString)) { System.out.println("ThreadName= " + this.getName() + "报错了 日期字符串: " + dateString + " 转换成的日期为: " + newDateString); } } catch (ParseException e) { e.printStackTrace(); } }}
package com.threadTest.thread.add.state.simpleDateFormat.test02;import java.text.SimpleDateFormat;/** * Created by sky on 2017/3/24. */public class Test { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[]{"2000-01-01", "2001-02-02", "2003-03-03", "2004-04-04"}; MyThread[] threadArray = new MyThread[4]; for (int i = 0; i < dateStringArray.length; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < dateStringArray.length; i++) { threadArray[i].start(); } }}
3)解决方法二
package com.threadTest.thread.add.state.simpleDateFormat.test03;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by sky on 2017/3/24. */public class DateTools { public static ThreadLocalt1 = new ThreadLocal (); public static SimpleDateFormat getSimpleDateFormat(String datePattern) { SimpleDateFormat sdf = null; sdf = t1.get(); if (null == sdf) { sdf = new SimpleDateFormat(datePattern); t1.set(sdf); } return sdf; }}
package com.threadTest.thread.add.state.simpleDateFormat.test03;import com.threadTest.thread.add.state.simpleDateFormat.test02.*;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/** * Created by sky on 2017/3/24. */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(); } }}
5、线程中出现异常的处理
package com.threadTest.thread.add.state.exception;/** * Created by sky on 2017/3/24. */public class MyThread extends Thread{ @Override public void run() { String username = null; //NullPointerException System.out.println(username.hashCode()); }}
package com.threadTest.thread.add.state.exception;/** * Created by sky on 2017/3/24. */public class Main01 { public static void main(String[] args) { MyThread myThread1 = new MyThread(); myThread1.setName("线程t1"); //setUncaughtExceptionHandler 对指定的线程对象设置默认的异常处理器 myThread1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程: " + t.getName() + " 出现异常: "); e.printStackTrace(); } }); myThread1.start(); MyThread myThread2 = new MyThread(); myThread2.setName("线程t2"); myThread2.start(); }}输出:线程: 线程t1 出现异常: Exception in thread "线程t2" java.lang.NullPointerException at com.threadTest.thread.add.state.exception.MyThread.run(MyThread.java:11)java.lang.NullPointerException at com.threadTest.thread.add.state.exception.MyThread.run(MyThread.java:11)
package com.threadTest.thread.add.state.exception;/** * Created by sky on 2017/3/24. */public class Main02 { public static void main(String[] args) { //setDefaultUncaughtExceptionHandler 为指定线程类的所有线程对象设置默认的异常处理器 MyThread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("线程: " + t.getName() + " 出现异常: "); e.printStackTrace(); } }); MyThread myThread1 = new MyThread(); myThread1.setName("线程t1"); myThread1.start(); MyThread myThread2 = new MyThread(); myThread2.setName("线程t2"); myThread2.start(); }}输出:线程: 线程t1 出现异常: java.lang.NullPointerException at com.threadTest.thread.add.state.exception.MyThread.run(MyThread.java:11)java.lang.NullPointerException线程: 线程t2 出现异常: at com.threadTest.thread.add.state.exception.MyThread.run(MyThread.java:11)