线程在不同的层次不同的说法与环境,就Java语言层次来说
线程状态
就线程来说不同的层面有不同的说法。
底层P(S),V(S)资源操作,编码层次对象锁操作,在Java层面对线程的状态定义如下
Java的枚举类Thread.State定义了6个线程的状态
线程状态。 线程可以处于以下状态之一:
- NEW 尚未启动的线程处于此状态。
- RUNNABLE 在 Java 虚拟机中执行的线程处于这种状态。
- BLOCKED 被阻塞等待监视器锁的线程处于这种状态。
- WAITING 无限期等待另一个线程执行特定操作的线程处于此状态。
- TIMED_WAITING 等待另一个线程执行操作达指定等待时间的线程处于此状态。
- TERMINATED 已退出的线程处于此状态。
一个线程在给定的时间点只能处于一种状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
线程之间的转换不多BB了,不想画图了
验证NEW、RUNNABLE、TERMINATED
NEW:线程为执行start
RUNNABLE:线程进入运行状态
TERMINATED:线程被消耗
//验证NEW、RUNNABLE、TERMINATED
class AA{
public static class TestThread extends Thread{
TestThread(){
System.out.println("调用构造Thread方法的线程"+Thread.currentThread().getState());
System.out.println("TestThread生成的线程"+ getState());
}
@Override
public void run() {
System.out.println("TestThread已经运行了"+currentThread().getState());
System.out.println("TestThread运行结束");
}
}
public static void main(String[] args) throws InterruptedException {
TestThread testThread = new TestThread();
System.out.println("main方法的testThread线程状态"+testThread.getState());
Thread.sleep(100);
testThread.start();
Thread.sleep(100);
System.out.println("main方法的testThread线程状态"+testThread.getState());
}
}
初始的main方法所在线程一直在RUNNABLE,但是testThread的线程从NEW变为RUNNABLE直到TERMINATED。生命周期结束
验证BLOCKED、WAITING、TIMED_WAITING
BLOCKED:阻塞,抢不到锁
WAITING:直观地使用wait方法
TIMED_WAITING:直观地使用sleep方法
class AB{
private Object object=new Object();
private void test(){
synchronized (object){
try {
Thread.sleep(1000);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
return;
}
}
public static void main(String[] args) throws InterruptedException {
AB ab = new AB();
Thread thread1 = new Thread(() -> ab.test());
Thread thread2 = new Thread(() -> ab.test());
Thread.sleep(100);
thread1.start();
thread2.start();
System.out.println(thread1.getState());
System.out.println(thread2.getState());
Thread.sleep(2000);
System.out.println(thread1.getState());
}
}
结果如下,使用了sleep之后thread1进入TIMED_WAITING状态。thread2抢不到锁进入BLOCKED状态,之后thread1又在WAITING状态,释放了锁之后thread2得到了进入TIMED_WAITING状态。因为不是守护线程,主线程main在这之后也不会结束
线程组
线程组类似于linux文件目录管理,利用树的结构进行管理
组中可以有组,也可以有线程对象
一级关联
树的层级为2,不再创建新的层级。
创建一个线程组,然后将部分线程归属到该组中,可以对线程对象进行有效的组织与规划
class BA {
public static void main(String[] args) {
ThreadGroup threadGroupA = new ThreadGroup("ThreadGroupA");
Thread thread1 = new Thread(threadGroupA, new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}));
Thread thread2 = new Thread(threadGroupA, new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}));
thread1.start();
thread2.start();
System.out.println(threadGroupA.getName()+" "+threadGroupA.activeCount());
System.out.println(threadGroupA.getName()+" "+threadGroupA.activeGroupCount());
}
}
线程组A有2个线程分别为线程1和线程3
多级线程的使用
public ThreadGroup(ThreadGroup parent, String name)构造方法构造
不贴代码了
其他线程组常用API
- 线程组自动归属特性
创建了一个线程组时候,如果没有显式给创建的线程组赋予父级线程组,那么会隐式地添加到创建该线程组的线程所在的线程组的子线程组 - 获取线程组父final ThreadGroup getParent()
//获取根线程组
class BB {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getThreadGroup().getName());
System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
System.out.println(Thread.currentThread().getThreadGroup().getParent().getParent().getName());
}
}
main线程所在线程组为main,其父线程组为system。再往上为NPE,所以JVM的顶级线程组为system
- 批量中断线程组中的所有线程 final void interrupt()
- 查看线程组中活动的线程数目 int activeCount()
- 将对此线程组及其子组中每个活动子组的引用复制到指定的数组中 int enumerate(ThreadGroup list[])
SimpleDateFormat
http://jtao.work/archives/di-er-shi-yi-zhang--he-xin-d-a-t-e--t-i-m-e-lei
除了java.util.Date和java.util.Calendar之外,SimpleDateFormat也是线程不安全的
- 代码测验
class CA {
private SimpleDateFormat simpleDateFormat;
private String date;
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
List<String> list = Arrays.asList("2021-01-01", "2021-02-01", "2021-03-01", "2021-04-01", "2021-05-01", "2021-06-01", "2021-07-01", "2021-08-01", "2021-09-01", "2021-01-10");
IntStream.iterate(0, i -> i = +1).limit(10).forEach(t ->
{
new Thread(() -> {
String date = null;
Date parse = null;
try {
Thread.sleep(100);
date = list.get(t);
parse = sdf.parse(date);
} catch (InterruptedException | ParseException e) {
e.printStackTrace();
}
String format = sdf.format(parse);
if (!date.equals(format)){
System.out.println("ThreadName="+Thread.currentThread().getName()+"转换错误:"+date+"转换的日期为:"+format);
}
}).start();
}
);
}
}
开启10个线程去跑10个,转换的结果乱七八糟,甚至直接报错格式转换异常
解决方法,多个SimpleDateFormat对象,或者加锁
JDK源码里面的注释写的很清楚了
- 多个SimpleDateFormat对象
在线程内部生成多个SimpleDateFormat对象,解决共享问题
结果也没有任何异常出现 - 加锁
synchronized锁或者lock锁等
- 使用ThreadLocal类来维护
http://jtao.work/archives/%E7%BA%BF%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1
线程中出现异常的处理
线程中run方法内catch处理也可以,但是会冗余,和Spring差不多的异常处理,全局异常处理
单个的异常处理
因为这个接口是Functional接口,所以可以写成lambda形式
class DA{
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
throw new RuntimeException("RuntimeException");
});
Thread thread2 = new Thread(()->{
throw new RuntimeException("RuntimeException");
});
Thread thread3 = new Thread(()->{
throw new Error("Error");
});
thread1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName());
e.printStackTrace();
}
});
thread2.setUncaughtExceptionHandler((t,e)->{
System.out.println(t.getName());
e.printStackTrace();
});
thread1.start();
thread2.start();
thread3.start();
}
}
前2个异常均被捕获后打印
默认的异常处理
static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
即如果A线程设置了setDefaultUncaughtExceptionHandler那么A线程创建的子线程会默认实现这个异常处理。
线程组的异常处理
线程组中的某个线程出现异常,默认不影响其他线程的执行。
可以重写该线程组的uncaughtException方法,出现异常的时候调用线程组的interrupt方法进行中断。
如果此时单个线程setUncaughtExceptionHandler()方法,则方法组的uncaughtException对其不起作用,不会执行
ThreadGroup threadGroup = new ThreadGroup("MyThreadGroup") {
@Override
public void uncaughtException(Thread t, Throwable e) {
//super.uncaughtException(t, e);
interrupt();
}
};
Comments | 0 条评论