多线程补充

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后活动的线程组: 2
java.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 ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>();
    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)
原文地址:https://www.cnblogs.com/beaconSky/p/6606425.html