多线程 补充

一、线程的状态

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();
    }
}

对象的异常处理优先

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值