一、线程的状态
1.1 Thread.State
线程的状态在该枚举类中表示
- BLOCKED
受阻塞并且正在等待监视器锁的某一线程的线程状态。 - NEW
至今尚未启动的线程的状态。 - RUNNABLE
可运行线程的线程状态。 - TERMINATED
已终止线程的线程状态。 - TIMED_WAITING
具有指定等待时间的某一等待线程的线程状态。 - WAITING
某一等待线程的线程状态。
二、线程各个状态的验证
2.1 new状态
线程还未启动的状态,即未调用start()方法。
public class MyThraed extends Thread{
@Override
public void run() {
System.out.println("你好呀");
}
public static void main(String[] args) {
MyThraed myThraed = new MyThraed();
System.out.println("当前线程的状态: " + myThraed.getState());
}
}
结果:
当前线程的状态: NEW
2.2 Runnable状态
线程进入可运行的状态
public class MyThraed extends Thread{
@Override
public void run() {
try {
System.out.println("当前线程的状态: " + Thread.currentThread().getState());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyThraed myThraed = new MyThraed();
myThraed.join();
myThraed.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.3 TERMINATED状态
线程销毁的状态
public class MyThraed extends Thread{
@Override
public void run() {
System.out.println("你好");
}
public static void main(String[] args) {
try {
MyThraed myThraed = new MyThraed();
myThraed.start();
Thread.sleep(1000);
System.out.println("当前线程状态为: " + myThraed.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
你好
当前线程状态为: TERMINATED
2.4 TIMED_WAITING
代表线程执行了Thread.sleep()方法
public class MyThraed extends Thread{
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyThraed myThraed = new MyThraed();
myThraed.start();
Thread.sleep(1000);
System.out.println("当前线程状态为: " + myThraed.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
当前线程状态为: TIMED_WAITING
2.5 BLOCKED
某个线程在等待锁的状态
同步方法类:
public class Trouble {
public synchronized static void show(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程1:
public class MyThread1 extends Thread {
@Override
public void run() {
Trouble.show();
}
}
线程2:
public class MyThread2 extends Thread {
@Override
public void run() {
Trouble.show();
}
}
测试类:
public class Test {
public static void main(String[] args) {
try {
MyThread1 myThread1 = new MyThread1();
MyThread2 myThread2 = new MyThread2();
myThread1.start();
myThread2.start();
Thread.sleep(1000);
System.out.println("当前线程状态为: " + myThread2.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread-0
当前线程状态为: BLOCKED
Thread-1
2.6 WAITING状态
线程执行了Object.waiting()方法的状态
宿主类:
public class Trouble {
static final ReentrantLock reentrantLock = new ReentrantLock();
static Condition condition = reentrantLock.newCondition();
static Boolean flag = true;
public static void MyWait() {
try {
reentrantLock.lock();
while (flag) {
condition.await();
}
flag = true;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
public static void MyNotify() {
try {
reentrantLock.lock();
while (flag) {
condition.await();
}
flag = false;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
线程1:
public class MyThread1 extends Thread {
@Override
public void run() {
Trouble.MyWait();
}
}
主类:
public class Test {
public static void main(String[] args) {
try {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
Thread.sleep(1000);
System.out.println("当前线程状态为: " + myThread1.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
当前线程状态为: WAITING
三、线程组
3.1 概念
可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组。组中还可以有线程。这样就可以批量的管理线程组对象或者线程对象。有效的对线程或者线程组对象就行组织。
3.2 线程对象关联线程组:1级关联
1级关联就是父对象中有子对象,但是没有子孙对象。
线程1:
public class MyThread1 extends Thread {
@Override
public void run() {
System.out.println("第一个线程");
}
}
线程2:
public class MyThread2 extends Thread {
@Override
public void run() {
System.out.println("第一个线程");
}
}
定义一个线程组,放入两个线程实例:
public class Test {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
MyThread2 myThread2 = new MyThread2();
ThreadGroup threadGroup = new ThreadGroup("我的第一个线程组");
Thread thread = new Thread(threadGroup, myThread1);
Thread thread1 = new Thread(threadGroup, myThread2);
thread.start();
thread1.start();
System.out.println("线程组中活动的线程数量" + threadGroup.activeCount());
System.out.println("线程组名称" + threadGroup.getName());
}
}
结果:
线程组中活动的线程数量2
线程组名称我的第一个线程组
第一个线程
第二个线程
3.3 线程对象关联线程组:多级关联
多级关联就是父对象中有子对象,子对象中再创建子对象。JDK提供了支持多级关联的线程树结构。
ThreadGroup类的enumerate()方法用于将每个活动线程的线程组及其子组复制到指定的数组中。 此方法使用tarray参数调用`enumerate()方法。
- main线程组中存放了一个线程组A,线程组A中又创建了一个线程aaaaaaa
public class Test {
public static void main(String[] args) {
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
//放入一个线程组对象
ThreadGroup a = new ThreadGroup(threadGroup, "A");
Runnable runnable = new Runnable(){
@Override
public void run() {
try {
//线程必须在运行状态才能受组管理
Thread.sleep(20000);
System.out.println("你好我也好");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//将该线程放到线程组中
Thread thread = new Thread(a,runnable);
thread.setName("aaaaaaa");
thread.start();
ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
int enumerate = Thread.currentThread().getThreadGroup().enumerate(threadGroups);
System.out.println("main线程中线程组数量是" + threadGroups.length + "名字是" + threadGroups[0].getName());
threadGroups[0].enumerate(threadGroups);
Thread[] threads = new Thread[threadGroups[0].activeCount()];
threadGroups[0].enumerate(threads);
System.out.println(threads[0].getName());
}
}
结果:
main线程中线程组数量是1名字是A
aaaaaaa
你好我也好
3.4 线程组自动归属特性
自动归属就是自动归到当前线程组中。也就是在实例化一个线程组时,如果不指定所属的线程组,则这个线程组自动归到当前线程所属组中。
测试类:
public class Test {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("A");
System.out.println("当前线程的名称" + Thread.currentThread().getName() + "当前线程组拥有的元素数量"
+ Thread.currentThread().getThreadGroup().activeCount());
ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
//把指定线程注中的线程组复制到指引的线程组数组中
Thread.currentThread().getThreadGroup().enumerate(threadGroups);
System.out.println(threadGroups[0].getName());
}
}
结果:
当前线程的名称main当前线程组拥有的元素数量2
A
可见,新建的线程组如果未指定其归属,则自动归为当前线程所在的组中。
3.5 获取根线程组
测试类:
public class Test {
public static void main(String[] args) {
System.out.println("根线程组的名称是" + Thread.currentThread().
getThreadGroup().getParent().getName());
}
}
结果:
根线程组的名称是system
说明:根线程组的名称是system
3.6 线程组中添加线程组
测试:
public class Test {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "A");
System.out.println("当前线程组拥有的子线程组数量: " + Thread.currentThread().
getThreadGroup().activeGroupCount());
}
}
结果:
当前线程组拥有的子线程组数量: 1
3.7 组内的线程批量停止
线程:
public class MyThread1 extends Thread {
public MyThread1(ThreadGroup group, String name){
super(group, name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始运行");
while (!this.isInterrupted()){
}
System.out.println(Thread.currentThread().getName() + "线程停止了");
}
}
测试类:
public class Test {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("A");
for (int i = 0; i < 5; i++) {
MyThread1 myThread1 = new MyThread1(threadGroup, "线程" + i);
myThread1.start();
}
threadGroup.interrupt();
}
}
结果:
线程0线程开始运行
线程0线程停止了
线程1线程开始运行
线程1线程停止了
线程2线程开始运行
线程2线程停止了
线程3线程开始运行
线程3线程停止了
线程4线程开始运行
线程4线程停止了
3.7 非递归取得线程组内所有对象
public class Test {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("A");
ThreadGroup threadGroup1 = new ThreadGroup(threadGroup, "B");
ThreadGroup threadGroup2 = new ThreadGroup(threadGroup, "C");
ThreadGroup threadGroup3 = new ThreadGroup(threadGroup1, "D");
ThreadGroup threadGroup4 = new ThreadGroup(threadGroup1, "E");
ThreadGroup threadGroup5 = new ThreadGroup(threadGroup4, "F");
MyThread1 myThread1 = new MyThread1(threadGroup, "1");
myThread1.start();
MyThread1 myThread2 = new MyThread1(threadGroup2, "2");
myThread2.start();
MyThread1 myThread3 = new MyThread1(threadGroup3, "4");
myThread3.start();
MyThread1 myThread4 = new MyThread1(threadGroup4, "3");
myThread4.start();
ThreadGroup[] threadGroups = new ThreadGroup[threadGroup.activeGroupCount()];
threadGroup.enumerate(threadGroups);
for (ThreadGroup group : threadGroups) {
System.out.println("线程组的名称" + group.getName());
}
Thread[] threads = new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);
for (Thread thread : threads) {
System.out.println("线程的名称" + thread.getName());
}
}
}
结果:
线程组的名称B
线程组的名称C
线程组的名称D
线程组的名称E
线程组的名称F
线程的名称1
线程的名称4
线程的名称3
线程的名称2
3.8 使线程具有有序性
使多个线程运行任务变得有序
四、SimpleDateFormat非线程安全
4.1 出现异常
SimpeDateFormat在多线程环境下容易出现错误的情况
class MyThread extends Thread {
SimpleDateFormat sdf;
String date;
public MyThread(SimpleDateFormat sdf, String date){
this.sdf = sdf;
this.date = date;
}
@Override
public void run() {
try {
Date parse = sdf.parse(date);
String string = sdf.format(parse).toString();
if(!string.equals(date)){
System.out.println("解析错误,将" + date + "解析成了" + " " + string);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试:
public class DateParseError {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String[] date = new String[]{"2012-3-12", "2012-3-2","2039-3-2","2012-2-4"};
MyThread[] myThreads = new MyThread[4];
for (int i = 0; i <4; i++) {
myThreads[i] = new MyThread(simpleDateFormat, date[i]);
}
for (int i = 0; i < 4; i++) {
myThreads[i].start();
}
}
}
结果:
解析错误,将2012-3-2解析成了 2201-02-02
解析错误,将2012-3-12解析成了 0001-02-02
解析错误,将2039-3-2解析成了 0001-02-02
解析错误,将2012-2-4解析成了 0001-02-02
4.2 解决方法1
每调用一个线程,创建一个SimpleDateFormat()对象。
public class DateTools {
public static Date parse(String format, String parse) throws ParseException {
return new SimpleDateFormat(format).parse(parse);
}
public static String format(Date date, String format){
return new SimpleDateFormat(format).format(date).toString();
}
}
线程:
class MyThread extends Thread {
String format;
String date;
public MyThread(String format, String date){
this.format = format;
this.date = date;
}
@Override
public void run() {
try {
Date parse = DateTools.parse(format, date);
String format = DateTools.format(parse, this.format);
if(!format.equals(date)) {
System.out.println("解析错误,将" + date + "解析成了" + " " + format);
}
else {
System.out.println("解析正确,将" + date + "解析成了" + " " + format);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
主类:
public class DateParseError {
public static void main(String[] args) {
String format = "yyyy-MM-dd";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String[] date = new String[]{"2012-03-12", "2012-03-02","2039-03-02","2012-02-04"};
MyThread[] myThreads = new MyThread[4];
for (int i = 0; i <4; i++) {
myThreads[i] = new MyThread(format, date[i]);
}
for (int i = 0; i < 4; i++) {
myThreads[i].start();
}
}
}
结果:
解析正确,将2012-03-12解析成了 2012-03-12
解析正确,将2039-03-02解析成了 2039-03-02
解析正确,将2012-03-02解析成了 2012-03-02
解析正确,将2012-02-04解析成了 2012-02-04
4.3 解决异常方法2
使用ThreadLocal
DateTools:
public class DateTools {
ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>();
public static SimpleDateFormat getSdf(String format){
SimpleDateFormat sdf = null;
if(sdf == null){
sdf = new SimpleDateFormat(format);
}
return sdf;
}
}
线程:
class MyThread extends Thread {
String format;
String date;
public MyThread(String format, String date){
this.format = format;
this.date = date;
}
@Override
public void run() {
try {
SimpleDateFormat sdf = DateTools.getSdf(format);
Date parse = sdf.parse(date);
String format = sdf.format(parse);
if(!format.equals(date)) {
System.out.println("解析错误,将" + date + "解析成了" + " " + format);
}
else {
System.out.println("解析正确,将" + date + "解析成了" + " " + format);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
主类:
public class DateParseError {
public static void main(String[] args) {
String format = "yyyy-MM-dd";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String[] date = new String[]{"2012-03-12", "2012-03-02","2039-03-02","2012-02-04"};
MyThread[] myThreads = new MyThread[4];
for (int i = 0; i <4; i++) {
myThreads[i] = new MyThread(format, date[i]);
}
for (int i = 0; i < 4; i++) {
myThreads[i].start();
}
}
}
结果:
解析正确,将2012-02-04解析成了 2012-02-04
解析正确,将2039-03-02解析成了 2039-03-02
解析正确,将2012-03-02解析成了 2012-03-02
解析正确,将2012-03-12解析成了 2012-03-12
五、线程中的异常
5.1 UncaughtExceptionHandler类
线程:
class MyThread extends Thread {
String name = null;
@Override
public void run() {
System.out.println(name.hashCode());
}
}
测试:
public class DateParseError {
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.setName("AAAA");
myThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程" + t.getName() + "出现了异常");
e.printStackTrace();
}
});
myThread.start();
Thread.sleep(1000);
System.out.println("就是这样");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
线程AAAA出现了异常
java.lang.NullPointerException
at com.ljs.miaosha.Other.MyThread.run(MyThread.java:12)
就是这样
setUncaughtExceptionHandler()方法是给指定线程对象设置的异常处理器,在Thread类中还可以设置setDefaultUncaaughtExceptionHandler()方法对所有的线程对象设置异常处理器。
全局异常处理器
public class DateParseError {
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.setName("AAAA");
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程" + t.getName() + "出现了异常");
e.printStackTrace();
}
});
myThread.start();
Thread.sleep(1000);
System.out.println("就是这样");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.2 线程组内处理异常
- 线程组内某一个线程出现异常,不会影响到线程组内的其他线程正常运行。
测试:
public class Exception {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("A");
MyThread myThread = new MyThread(threadGroup, "a");
myThread.start();
NormalThread b = new NormalThread(threadGroup, "b");
b.start();
}
}
结果:
准备出错
你好
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at MyThread.run(MyThread.java:12)
5.3 一个线程出现异常,其他线程都停止
思路:创建一个继承ThreadGroup的类,重写uncaughtException()方法
线程的构造方法是调用父类的构造方法
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();
}
}
正常线程:
public class NormalThread extends Thread {
public NormalThread(ThreadGroup threadGroup, String name){
super(threadGroup,name);
}
@Override
public void run() {
while (!isInterrupted()){
}
System.out.println(getName() + "已经被停止了");
}
}
会出错的线程:
public class MyThread1 extends Thread {
public MyThread1(ThreadGroup threadGroup, String name){
super(threadGroup,name);
}
@Override
public void run() {
System.out.println(1/0);
while (!isInterrupted()){
}
System.out.println("出现异常的线程也会被停止");
}
}
测试:
public class Exception {
public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup("A");
MyThread1 myThread = new MyThread1(threadGroup, "a");
myThread.start();
NormalThread b = new NormalThread(threadGroup, "b");
b.start();
}
}
结果:
b已经被停止了
Exception in thread "a" java.lang.ArithmeticException: / by zero
at MyThread1.run(MyThread1.java:7)
5.4 线程异常处理的传递
对象的异常处理:
public class ObjectDo implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("对象的异常处理");
e.printStackTrace();
}
}
全局异常处理:
public class staticDo implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("对象的静态处理");
}
}
线程组的异常处理:
5.4.1 对象异常处理与全局异常处理的比较
测试用例1:
对象异常处理与全局异常处理的比较
public class ThreadGroup_Test {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
Thread.setDefaultUncaughtExceptionHandler(new staticDo());
myThread1.setUncaughtExceptionHandler(new ObjectDo());
myThread1.start();
}
}
结果:
java.lang.ArithmeticException: / by zero
对象的异常处理
at MyThread1.run(MyThread1.java:10)
说明:具体对象异常的处理优先于全局异常处理
5.4.2 全局异常处理,线程组异常处理的比较
测试用例:
public class Exception {
public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup("A");
MyThread1 myThread = new MyThread1(threadGroup, "a");
myThread.start();
Thread.setDefaultUncaughtExceptionHandler(new staticDo());
NormalThread b = new NormalThread(threadGroup, "b");
b.start();
}
}
结果:
线程组的异常处理
对象的静态处理
没有对象的异常处理,线程组的异常处理和全局异常处理都会进行处理。
5.4.3 对象异常处理,全局异常处理,线程组异常处理的比较
public class Exception {
public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup("A");
MyThread1 myThread = new MyThread1(threadGroup, "a");
myThread.start();
myThread.setUncaughtExceptionHandler(new ObjectDo());
Thread.setDefaultUncaughtExceptionHandler(new staticDo());
NormalThread b = new NormalThread(threadGroup, "b");
b.start();
}
}
对象的异常处理优先