【JavaEE】Thread类

news2024/11/21 1:37:39

目录

前言

1、创建一个线程 

1.1、 体会多线程的执行

 1.2、体会单线程的执行

 1.3、sleep方法(休眠)

1.4、通过第三方程序来观察线程详情

1.5、创建线程的方式 

1.5.1、继承Thread类,重写run方法来创建线程

1.5.2、实现Runnable接口,重写run方法来创建线程

1.5.3、继承Thread类,使用匿名内部类

1.5.4、继承Runnable接口,使用匿名内部类(定义在类里面的类)

1.5.5、使用lambda表达式来创建线程

2、Thread类及常见方法

2.1、Thread的常见构造方法

2.2、Thread类的几个常见属性

 3、启动一个线程(start)

4、中断一个线程

4.1、设置结束标志位 

 5、等待一个线程(join)

6、获取当前线程的引用

7、休眠当前线程

8、线程的状态

 8.1、线程状态之间的转换


前言

Java标准库中Thread类可以视为是对操作系统提供的API进行了进一步的抽象和封装。

1、创建一个线程 

Java标准库中提供了一个类Thread能够表示一个线程,但是我们在示例化的时候,并不是直接创建Thread这个类的对象,而是创建一个Thread类的子类,重写run(线程的入口方法)。创建子类的对象,用父类的引用指向这个子类对象。

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello world");
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        //启动线程
        t.start();
        //启动线程之后,相当于在工厂(进程)中开启了一个新的流水线(执行流/线程),新的流水线开始并发的执行另外一个逻辑了
    }
}

点击运行程序,其实是idea创建了一个新的Java进程,这个Java线程来执行我们自己写的代码。

这个Java进程中存在两个线程。

  1. main方法所对应的线程(一个线程里至少存在一个线程)也可以称为主线程
  2. 通过t.start创建的新的线程。

1.1、 体会多线程的执行

 上述的代码我问并不能体会到“每个线程是一个独立的执行流”,我们将上述代码改动以下,在两个类中加上一个死循环,来观察他们的执行结果。

//体会多线程

//这个代码按理来说,执行了start方法之后,进入到MyThread对象中执行run方法内容,会死循环出不去了。也就是说在打印的时候只能输出hello t,现在我们来观察以下它的执行结果。

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("hello t");
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        //启动线程
        t.start();//这个start的作用是启动了一个新的线程,来执行MyThread中的内容。
        while(true){
            System.out.println("hello main");
        }
    }
}

 结果中,两个死循环中的打印结果都有,从这里我们可以看出来,多线程是并发(并发+并行)执行的。start方法用来开启新的线程。

 1.2、体会单线程的执行

在main中直接调用run方法,run方法不会创建新的线程,run方法的执行是在main方法的线程中执行的。

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("hello t");
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        //start 会创建一个线程
        //  t.start();
        //run不会创建新的线程,run是在main 中执行的
        t.run();
        while(true){
            System.out.println("hello main");
        }
    }
}

 此处的代码中没有使用start方法创建新的线程,两个死循环是在同一个线程中的,执行第一个死循环的时候,进入之后就出不来了,无法进入到第二个循环中。

❗❗❗总结:

  1. 这里的run方法可以这样理解,main方法是主线程的入口方法,对象使用start方法创建新的线程,新的线程也需要一个入口方法来进入执行。所以run可以和main方法的这个作用类比。让CPU核心执行这个线程的入口。
  2. start方法会调用操作系统的API,创建新的线程。新的线程里调用run方法。通过多线程的代码来看,t对象在调用了start这个方法之后,并没有调用run方法,但是程序在执行的时候,run方法中的结果被打印出来了。这里的重写的方法是不需要我们手动调用的,已经有其他代码来调用了。

 1.3、sleep方法(休眠)

sleep是Thread类的静态方法,直接使用Thread直接调用。sleep方法的参数单位时ms。1s = 1000ms

上述两个代码运行起来打印结果的速度太快,我们可以使用sleep方法,在程序执行时,让其休眠一段时间,有利于我们的观察。

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("hello t");
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){//Interrupted表示打断/中断 —— 意思就是sleep在睡眠过程中,还没有到时间,就被唤醒
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args)  {
        Thread t = new MyThread();
        //start 会创建一个线程
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 可以看到这里的打印速度就会慢很多,有利于观察结果。

此处的交替也不是严格意义上的交替,每一秒过后,是先打印main还是先打印t,是不确定的。多个线程在CPU上调度执行的顺序是不确定的(随机的)。

1.4、通过第三方程序来观察线程详情

我们找到jdk安装的地方,然后bin目录下找到这个程序,用这个jdk提供的工具,就能够给我们查看出Java进程里面的线程的详情。

 当我们的代码运行起来之后,点击这个程序,就可以看见当前本地的一些进程。

 

 ❗❗❗注意:jconsole只能分析Java进程,不能识别非Java写的进程。

 

 点击我们程序中的main和Thread-0,来查看这个线程执行的具体情况。这里我们主要关注的就是堆栈跟踪。堆栈跟踪描述的就是当前这个两个线程中的代码执行到哪里了。之后我们在写这些多线程代码的时候出现bagel,需要调试的情况下,参考调用栈是一个有效的方法。

1.5、创建线程的方式 

1.5.1、继承Thread类,重写run方法来创建线程

上述的代码中使用的都是进程Thread类,重写run方法的方式来创建线程的,所以这里不在进程过多的说明了。


1.5.2、实现Runnable接口,重写run方法来创建线程

 Runnable字面意思是可运行的。使用Runnable描述一个具体的任务,Runnable接口使用run方法来描述。

第一种写法是使用Thread的run来描述线程的入口

第二种写法是使用Runnable interface来描述线程的入口

虽然描述线程入口的方式存在差别,但是起到的效果是一样的。

class MyRunnable implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("hello t");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();//这里创建一个实现Runnable接口的类的对象
        //这里直接创建Thread类的对象,不用实现Thread的子类。
        Thread t = new Thread(runnable);//将这个对象作为Thread类的构造方法的参数
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

第一种写法和第二种写法之间,没有本质的区别,只是描述线程入口的方式不同而已,这两种写法也没有高下之分,使用那种都可以。


1.5.3、继承Thread类,使用匿名内部类

我们通过在创建Thread类的时候,在后边写一个大括号({}),大括号中来写属性或者方法。这就是在创建类的时候,使用匿名内部类。

public class ThreadDemo3 {
    public static void main(String[] args) {
        //new 表示创建一个对象这个动作
        //Thread(),对象名 表示这个匿名内部类继承了这个类
        Thread t = new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.5.4、继承Runnable接口,使用匿名内部类(定义在类里面的类)

内部类是定义在类里面的有名字的类,匿名内部类是定义在类里面没有名字的类。 

public class ThreadDemo4 {
    public static void main(String[] args) {
        //此处实现的匿名内部类是实现了Runnable接口的
        //这里的new表示的是创建对象的动作,
        //Runnable(),接口名称就是匿名内部类需要实现的那个接口
        //{..}表示的是匿名内部类的内容
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.5.5、使用lambda表达式来创建线程

上述的四种创建线程的方法,在实际中并不是使用最多的方法,而是lambda表达式是使用最多的。使用lambda表达式,是最简单最直观的写法。

  • lambda表达式本质就是一个匿名函数(没有名字的函数,这种一般都是一次性的),Java里面方法是无法脱离类的,在Java里边lambda就是一个例外。
  • ❗❗❗lambda表达式的基本写法()->{方法体};()中可以放多个参数,如果只有一个参数,()可以省略。{}中如果只有一行代码,也可以省略。

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(true){
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2、Thread类及常见方法

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread对象与之关联。

每个执行流(线程),需要有一个对象来描述,而Thread类的对象就是用来描述一个线程执行流的JVM会将这些Thread对象组织起来,用于线程调度,线程管理。

2.1、Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name )创建线程,并命名
Thread(Running target,String name)使用Runnable对象创建对象,并命名。

前两个构造方法,在之前的代码中已经使用过了,不做解释,这里我们来了解一下后面两个构造方法。后面两个两个构造方法中的参数name的作用就是给线程起了一个名字,这里的名字不影响线程的执行,只是方便咱们再调试的时候,快速找到咱们关心的线程。

package threading;

public class ThreadDemo6 {
    public static void main(String[] args) {
        //将这个lambda表达式作为Thread的一个参数
        Thread t = new Thread(()->{
            while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"我的名字");
        t.start();

    }
}

 可以看见这些线程中,我们对上述Java代码所在的线程进行了命名。

2.2、Thread类的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台运行isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID:是线程的唯一标识,每个线程都有自己的ID
  • 名称:就像上述我们给线程命名
  • 是否为后台线程true 表示为后台线程,false 表示为前台线程前台线程会阻止Java进程结束,必须得Java进程中所有的前台线程都执行完,Java进程才能结束后台线程不阻止Java进程结束,哪怕后台线程还没执行完,Java进程该结束就结束了我们创建的线程默认是前台的可以通过setDaemon设置成后台的
public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(true){

            }
        });
        //默认是前台线程,也就是设为false
        //此时这个线程会阻止进程结束
        //t.setDaemon(false);

        //改成true编程后台线程,不影响进程的结束
        t.setDaemon(true);

        t.start();
    }
}
  • 线程是否存活:描述的是系统内核里的那个线程是否存活一种是线程的入口方法执行完毕,此时系统中的对应线程就没了,此时调用该线程的isALive就是false,表示这个线程已经不再存活了另一种是线程对象已经创建好了,但是还没有执行调用start,那么这个时候它的isALive也是false.这个线程也是不存活的状态。
public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello t");
        });
        t.start();
        try {
            //上述t线程没有进行任何循环和sleep,意味着里面的代码会迅速执行完毕,
            //也就意味着,main线程如果sleep结束,此时t基本上就是已经执行完了的状态,此时t对象还在
            //但是在系统中对应的线程已经结束了
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//在main线程中过一秒在调用isALive方法,则这个线程已经结束,结果为false
        System.out.println(t.isAlive());
    }
}

上述中说到main线程sleep结束,此时t基本上就是已经执行完了的状态,这里为什么是基本上,因为他可能存在极端情况,主线程sleep结束了,t线程仍然还没有去CPU上执行。这里主要是系统对于线程的调度是随机的。比如我们有些时候,在使用机器的时候,点击某个窗口的时候,提示"未响应"。

 3、启动一个线程(start)

  • start方法:他是开启/创建一个线程。
  • run方法:表示的是新线程的入口方法,这个方法是被系统自动调用起来的,程序员不需要调用这个方法。

4、中断一个线程

中断线程这里表示的意思就是让一个线程终止,本质上来说,让一个线程终止,办法就一种,让该线程的入口方法执行完毕。让一个线程入口方法执行完毕的方法有很多。

4.1、设置结束标志位 

1️⃣第一种手动给线程中设定一个结束标志位。

public class ThreadDemo9 {
//将isQuit作为线程结束标志位
    public static boolean isQuit = false;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(!isQuit){
                System.out.println("hello t");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t 线程结束");
        });

        t.start();
        //在主线程,修改isQuit
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;

    }
}

❓❓❓如果将上述代码中的isQuit从成员变量改成局部变量(放到main方法中),这个代码还能正常运行吗?


❗❗❗不能 这里就涉及到了lambda表达式的知识点 变量捕获

lambda表达式能否访问外面的局部变量?可以——变量捕获语法规则

Java要求变量捕获,捕获的是变量必须是被final或者"实际final"修饰。被final修饰变量会变成常量,实际final就是变量没有用final修饰,但是代码中并没有做出修改。


上述代码中,isQuit并不是被final修饰的局部变量,或者是实际final变量。因为我们的isQuit在主线程中进行了修改,所以我们直接将isQuit写成了成员变量,成员变量不受上述语法规则的限制。

 上述的代码是我们自己创建了一个变量来控制循环,其实Thread类内部内置了一个标志位,让我们更方便的实现上述效果——设置标志位。

2️⃣通过Thread类内置的方法设置结束标志位

public class ThreadDemo10 {
    public static void main(String[] args)  {
        Thread t = new Thread(()->{
            //currentThread()是获取到当前线程的实例
            //此处currentThread得到的对象就是t
            //isInterrupted()可以理解为t对象里自带的一个标志位,起始为false
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });


        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            //打印出异常方法的调用栈
            e.printStackTrace();
        }
        //把t内部的标志位给设置成true
        t.interrupt();
    }
}

 此时我们看代码的执行结果,发现,主线程中睡眠了3s后,调用t.interrupt方法的时候,线程并没有真的结束,而是打印了异常这个星系,又继续执行了。

❗❗❗这里我们就需要对interrupt这个方法有一个了解。他又两个作用。

  1. 设置标志位为true
  2. 如果该线程正在阻塞中(比如正在执行sleep),此时就会把这个阻塞状态唤醒,通过抛出异常的方式让sleep立即结束。

换言之,当两个线程,主线程走到interrupt这个方法这里,另一个线程走到打印"hello t"这里,那么当interrupt被执行,另一个线程中,会直接设置标志位。但是当主线程里走到interrupt,另一个线程走到sleep函数,当主线程中interrupt被执行之后,sleep会被直接中断,让休眠结束。通过抛异常的方式唤醒让sleep结束。

❗❗❗又存在要给问题,interrupt执行之后标志位变为true,为什么在显示的结果中抛出异常之后,程序还会继续被执行。

这是因为sleep在被唤醒的时候,sleep会自动的把isInterrupted标志位给清空(将true还原为false)

 ❓❓❓说到这里有的同学就会问,在whlie循环中,第一次执行到sleep是被唤醒,抛出了异常,而循环到第二次为什么没有排除异常??

  • 这个问题的产生,是因为我们在思考上述代码的时候,是以单线程的思路来考虑的,而不是以多线程的思路考虑的。
  • 我么要注意多线程在执行的时候是以并发(并行+并发)的方式运行的,以上述代码为例,上述代码中存在两个线程,可以理解为两个线程是同时运行的(电脑对线程的调度非常快速,我们人类是感知不到的)多线程的执行顺序是分两路,自己执行自己的代码
  • 主线程中的interrupt只执行了一次,主线程结束了,只剩一个线程在运行了。也就是interrupt对标志位只修改了一次,然后剩下的线程在运行的时候while循环中的条件只能是true,也就是说isInterrupted标志位只能是false.

❓❓❓为什么sleep要清空标志位呢?

❗❗❗目的就是为了让线程自身能够对于线程何时结束,有一个明确的控制。

当前interrupt方法,效果不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要立即结束还是等会结束,都是代码来灵活控制的,interrupt只是单纯的通知而不是命令。


1️⃣无视要求

就上述的代码而言,我们将try-catch中的打印异常方法调用栈这行注释掉。那么我们的代码就将interrupt的通知无视掉了。

public class ThreadDemo10 {
    public static void main(String[] args)  {
        Thread t = new Thread(()->{
            //currentThread()是获取到当前线程的实例
            //此处currentThread得到的对象就是t
            //isInterrupted()可以理解为t对象里自带的一个标志位
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                   // e.printStackTrace();
                   
                }
            }
        });
        t.start();

2️⃣立即执行

这种是在上述代码中try-catch 中加上一个break。

public class ThreadDemo10 {
    public static void main(String[] args)  {
        Thread t = new Thread(()->{
            //currentThread()是获取到当前线程的实例
            //此处currentThread得到的对象就是t
            //isInterrupted()可以理解为t对象里自带的一个标志位
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;//将break放在这里,是当sleep抛出异常之后,执行catch中的代码,在这里结束。
                }
//break;如果将break放在这里,会导致while循环执行一次就结束。

            }
        });
        t.start();

3️⃣等会执行

在catch中再写一个try-catch。

public class ThreadDemo10 {
    public static void main(String[] args)  {
        Thread t = new Thread(()->{
            //currentThread()是获取到当前线程的实例
            //此处currentThread得到的对象就是t
            //isInterrupted()可以理解为t对象里自带的一个标志位
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                  
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }

            }
        });

❗❗❗总结:

  • interrupt方法和sleep不是搭配使用,这里sleep只是用作举例 ,Thread这里但凡是涉及到阻塞功能的方法都和sleep有类似的效果。
  • 多线程的执行方式是,兵分多路,每个线程都同时执行自己线程中的代码。
  • 使用isInterruped方法和interrupt方法设置结束标志位,当线程发生阻塞时,interrupt将阻塞(例如sleep)唤醒时,相应的isInterrupted的标志位就会被清空,将true——》false. interrupt将标志位由false改为true。
  • interrupt存在两种作用,改变标志位的值,唤醒阻塞(sleep)。
  • sleep存在两种作用,休眠,清空标志位。

 5、等待一个线程(join)

线程之间时并发执行的,操作系统对于线程的调度是无序的,无法判定两个线程谁先执行结束,时候执行结束。

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello t");
        });
        t.start();
        System.out.println("hello main");
    }
}

这两个线程到底是先输出hello main还是hello t,我们无法确定。

这个代码实际执行的时候,大部分情况下都是先执行出hello main(因为线程创建也有开销),但是不排除特定情况下,主线程hello main没有立即执行到。谁先执行谁后执行,这些都是不确定的,那么结束的顺序也是不确定的。所以为了明确结束顺序,这个时候我们就要说到join方法。

public class ThreadDemo11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("hello t");
        });
        t.start();

        t.join();

        System.out.println("hello main");
    }
}

❗❗❗注意:

  • 这里的谁等谁很关键,这里的join是在main中被调用,所以是main线程等t线程先结束,再往下执行。
  • 如果再t.join执行的时候,t线程还没结束 ,main线程就会阻塞等待(代码走到这一行就停下来了)。当前这个main线程暂时不参与cpu的调度执行了
  • 这里调用join之后,调用join的线程停止,别的线程(后台线程)不受影响,就比如处理调试信息或者垃圾回收的线程都不受影响。

❗❗❗ 理解t.join()

  • main线程调用t.join()的狮虎,如果t还在执行,此时main线程阻塞,知道t执行完毕(t的run执行完了),main才从阻塞中解除,才继续执行
  • main线程调用t.join的时候,如果t已经结束了,此时join不会阻塞,就会立即往下执行。

 join方法的两种写法,带参数和不带参数。

方法说明
public void join()等待线程结束(死等)
public void join(long millis)等待线程结束,(有时间限制,最多等多长时间)

6、获取当前线程的引用

方法说明
public static Thread currentThread()返回当前线程对象的引用

这个方法在上面的设置结束标志位的时候已经用过了。

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

作用就是在那个线程中调用,获取的就是那个线程的实例。


7、休眠当前线程

这个方法大家都相当熟悉了,在这篇博客中,每个代码中都有它的身影。所以我么不做过多的说明。sleep方法,只能保证实际休眠时间是大于等于参数设置的休眠时间的,因为线程的调度是不可控制的。


8、线程的状态

操作系统里面的线程,自身是有一个状态的,但是Java Thread是对系统线程的封装,把这里的状态又进一步的精细化了。

1️⃣NEW状态:系统中的线程还没有创建出来呢,只是有个Thread对象。

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("hello");
        });
        //在启动之前,获取线程状态,NEW
        System.out.println(t.getState());
        t.start();
    }
}

2️⃣ TERMINATED状态:系统中的线程已经执行完了,Thread对象还在。

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("hello");
        });

        t.start();

        //这里休眠2秒,确保t线程已经执行完了
        Thread.sleep(2000);
        System.out.println(t.getState());
    }
}

 3️⃣RUNNABLE状态:就绪状态。存在两种可能(1、正在CPU上运行。2、准备好随时可以去CPU上运行)

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(true){
                //为了防止hello把线程状态冲没了,先注释掉
                //System.out.println("hello");
            }
        });
        //在启动之前,获取线程状态,NEW
        System.out.println(t.getState());
        t.start();

        Thread.sleep(2000);
        System.out.println(t.getState());
    }
}

 4️⃣TIMED_WAITING:指定时间等待就会触发这个状态(比如sleep方法)

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(true){
                //为了防止hello把线程状态冲没了,先注释掉
                //System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //在启动之前,获取线程状态,NEW
        System.out.println(t.getState());
        t.start();

        Thread.sleep(2000);
        System.out.println(t.getState());
    }
}

 5️⃣BLOCKED:表示等待锁出现的状态。

6️⃣WAITING:使用wait方法时出现的状态。

 8.1、线程状态之间的转换

一条主线,三条支线。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 查看系统版本

文章目录 一、查看Linux内核版本的命令二、查看Linux系统发行版本的命令三、 延伸: 一、查看Linux内核版本的命令 cat /proc/version 此命令可以查看正在运行的内核版本信息。/proc 目录存储的是当前内核运行状态的一系列特殊文件,包括:内存…

electron源码保护

electron 程序发布后,如果未对程序做保护,则极容易受到破坏,比如被轻松破解密码,或者被修改程序,所以必须对程序做一些安全防护。虽然没有100%的安全防护,但是提升破解难度,直至破解代价超出了范…

UML图中的domain model,object model,system sequence diagram以及interaction diagram

UML图(Unified Modeling Language,统一建模语言)是一种用于描述、可视化、构建和记录软件系统的标准化建模语言。在UML中,有很多类型的图,其中包括领域模型(Domain Model)、对象模型&#xff08…

拥抱智能时代:初探RFID系统

在数字化时代,人们越来越追求高效率和高质量的体验,以获得更快乐、更好的生活。RFID系统作为一项智能化管理技术,正越来越广泛地应用于各个领域,以提高效率和质量。本文将介绍RFID系统的基本概念、工作原理和实际应用案例&#xf…

OpenAI的编程语言和框架,给程序员带来了帮助有哪些

OpenAI 是一个人工智能开发公司,成立于2015年,总部位于美国旧金山。这家公司致力于研究和开发先进的人工智能技术,旨在将这些技术应用到解决全球一些最棘手的问题上。 OpenAI 以其卓越的技术和实验室出品的 groundbreaking AI papers 而闻名…

Android焦点流程梳理

作者:Cy13er 前言 最近在看一些焦点处理的问题,认真处理起来发现不跟着源码自己走一遍焦点相关的流程,对于问题的分析上会比较困难。所以本文主要对焦点流程进行一次梳理,在处理类似问题时也可以作为手册阅读。 起源 一切都要从…

Apache Kafka 进阶(一)

官网 Apache Kafka是一个开源的分布式事件流平台,被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。 核心能力 高吞吐量 在网络有限的吞吐量下,使用延迟低至2ms的机器集群交付消息。可扩展性 将生产集群扩展到1000个代理&#xff0c…

SQLite安装配置

1.什么是 SQLite? SQLite是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite是一个增长最快的数据库引擎,这是在普及方面的增长,与它的尺寸大小无关。SQLite源代码不受版权限制。 SQLite是…

Linux 五种网络IO模式(阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO)

Linux网络编程中,有五种网络IO模式,分别是阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO; 虽然说不能全都认识得很透彻,但至少得都知道一点! 开始之前,先了解以下同步IO和异步IO; 1. 同步…

探索AIGC创新实践,亚马逊云科技与全球咨询合作伙伴携手同行

AIGC(AI Generated Content,人工智能生成内容),已经成为全球出圈的科技热点。各行各业都在重新审视和思考AIGC的创新价值、未来趋势和成功实践,力争在这波热潮下寻找更多创新的可能性,重塑行业格局。 在AIGC领域,亚马…

无代码资讯|ChatGPT新功能曝光;Mendix与亚马逊云科技底层融合;无代码开发平台Appy Pie推出内置AI

栏目导读:无代码资讯栏目从全球视角出发,带您了解无代码相关最新资讯。 Top3大事件 1、ChatGPT 新功能曝光,GPT-4 迎来 AGI 历史性时刻! 北美时间4月20日,Open AI联合创始人Greg Brockman受邀出席 “2023TED” 大会&…

手写【深拷贝】

JS中深拷贝的实现 JSON.parse(JSON.stringify())递归实现深拷贝 使用JSON.parse(JSON.stringify()) 实现 无法拷贝 函数、正则、时间格式、原型上的属性和方法等 递归实现深拷贝 es5实现深拷贝 源对象 const obj {name: 张桑,age: 18,hobby: [{name: 篮球,year: 5,loveSta…

极简爬虫通用模板

网络爬虫的一般步骤如下: 1、确定爬取目标:确定需要爬取的数据类型和来源网站。 2、制定爬取策略:确定爬取哪些网页、如何爬取和频率等。 3、构建爬虫程序:使用编程语言(如Python)实现爬虫程序&#xff…

【python】列表、字典、元组与集合的特点以及对比

一、列表(List) 1. 列表的特点 数据按顺序存储列表有正序、倒序两种索引列表可存储任意类型的数据,并且允许重复。 2. 列表的遍历: lst[1,2,3] for i in range(len(lst)):print(lst[i],end" ")3. 列表的缺点&#x…

虹科方案 | HK-TrueNAS:音频协作的理想存储

一、虹科HK-TRUENAS 非常适合 AVID PRO TOOLS™ 专业音频编辑和大多数媒体和娱乐 (M&E) 工作流程从录制开始,经过后期制作,最后进入播放。这一过程可能需要几个月的时间来拍摄一部大型的电影,也可能需要几个小时甚至几分钟的时间来播放最…

Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购、工程交易等业务的企业

招投标管理系统-适合于招标代理、政府采购、企业采购、工程交易等业务的企业 招投标管理系统是一个用于内部业务项目管理的应用平台。以项目为主线,从项目立项,资格预审,标书编制审核,招标公告,项目开标,项…

使用篇丨链路追踪(Tracing)很简单:链路拓扑

作者:涯海 最近一年,小玉所在的业务部门发起了轰轰烈烈的微服务化运动,大量业务中台应用被拆分成更细粒度的微服务应用。为了迎接即将到来的双十一大促重保活动,小玉的主管让她在一周内梳理出订单中心的全局关键上下游依赖&#…

反射~~~

文章目录 反射反射获取Class类对象反射获取构造器对象反射获取成员变量对象反射获取方法对象反射的作用绕过编译阶段为集合添加数据通用框架的底层原理 反射 反射获取Class类对象 getClass()方法为Object类中的成员方法 反射获取构造器对象 parametTypes为参数的类对象 获得类的…

智安网络|网络安全威胁越来越多,教你如何全方面应对

随着互联网的普及和发展,各大网站已经成为人们获取信息和交流的主要平台。然而,随着网络攻击和恶意软件的威胁不断增加,网站经常成为攻击者的目标。因此,在建立和维护网站系统时,必须采取强大的安全措施。 一、网站系…

阅读有感重庆rcgl

1.json转为对应的泛型集合 List<String> resourceList JSON.parseArray(JSON.toJSONString(obj), String.class); 2.集合转换为数组 String[] roles (String[])resourceList.toArray(new String[0]); 3.json转换为对应的javabean SLoginRule loginRule (SLoginRul…