java [多线程基础 二】

Thread类

线程常见构造方法

其中第三、第四构造方法,可以给线程起名字。

如果不起名字默认Thread-0、Thread-1......

Thread 常见属性

ID式线程的唯一标识,不同线程不会重复

状态表示线程当前所处的情况(阻塞 / 就绪)

优先级高的线程理论上更容易被调度

关于后台线程,JVM会在一个进程的所有非后台线程结束后,才会结束运行.

是否存活,简单的理解是run方法是否运行结束了

代码中,创建的Thread对象的生命周期,和系统中实际线程的生命周期可能不同,可能会出现Thread对象仍然存在,但是内核中的线程不存在的情况

1)调用start方法之前,系统中,还没创建线程

2)线程的run执行完毕后,线程就结束了,但Thread对象,仍然存在

后台线程

某个线程在执行过程中,不能阻止进程结束(虽然线程还在执行,但进程要结束了,此时这个线程会随着进程的结束而结束),这样的线程就是"后台线程".

前台线程

某个线程在执行过程中,能够阻止进程结束,这样的线程就是"前台线程".

线程的核心操作

1. 创建线程 start

start 和 run之间的区别

start:调用系统函数,在系统内核中,创建线程,创建好的线程再来单独执行run。(此处的 start,会根据不同的操作系统,来分别调用不同的api)

run:描述线程要执行的任务,也可以称为"线程的入口"

调用 start ,不一定是main线程调用。任何线程都可以创建其他线程,如果系统资源充裕,可以任意创建线程。

一个Thread对象,只能调用一次 start,如果多次调用,会报以下异常

(一个Thread对象,只能对应系统中的一个线程)

2. 线程的中断

1.自定义变量作为标志位

java 复制代码
​
public class Demo17 {
    private static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(()->{
        while(!isQuit) {
            System.out.println("hello,Thread");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("线程执行结束");
    });

    t.start();
    Thread.sleep(2000);
    System.out.println("main 线程尝试终止 t 线程");
    isQuit = true;

    
}
}

​

2. 使用Thread.Interrupted()或Thread.currentThread().isInterrupted()

Thread 内部包含了一个boolean 类型的变量作为线程是否被中断的标记

初始情况下,变量为false

但若有其他线程,调用interrupt方法,会设置上述标志位

java 复制代码
public class Demo18 {

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //获取线程的引用
            Thread currentThread = Thread.currentThread();

            while (!currentThread.isInterrupted()) {
                System.out.println("hello,Thread");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });


        t.start();

        Thread.sleep(3000);
        //在主线程中,控制 t 线程终止,设置上述标志位
        t.interrupt();
    }

urrentThread() 是 Thread 类的静态方法.调用此方法就能获取到调用此方法的线程实例(作用类似this)

但执行代码时,出现了以下异常

由于catch中 默认代码再次抛出异常,但再次抛出的异常,没有再次被catch,那么进程就直接异常终止

根本原因是 sleep/wait/join 等阻塞的方法被唤醒之后,会清空刚才设置的interrupted标志位,导致代码一直在循环

因此,要想结束循环,结束线程,需要在 catch 中加 return / break .

线程等待

操作系统,针对多个线程的执行,是一个"随机调度,抢占式执行"的过程。而线程等待,就是在确定两个线程的"结束顺序".

具体逻辑:让后结束的线程,等待先结束的线程即可,此时后结束的线程就会进入阻塞,一直到先结束的线程,真的结束了,阻塞才接触

假设现在有线程a,b

在a线程中调用 b.join 意思就是让a线程等待b线程,直到b线程结束,然后a再继续执行.

java 复制代码
public class Demo19 {

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{

            for(int i = 0; i < 3 ; i++){
                System.out.println("这是t线程");

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

        t.start();
        System.out.println("主线程开始等待");
        //main线程开始等待 t线程
        try {
            t.join();
        }catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程等待结束");

    }
}

任何线程之间都是可以互相等待的,线程等待不止可以存在于两个线程之间,也可以同时等待多个其他线程,或若干线程之间互相等待.

创建t1,t2线程,并让t2等待t1,主线程等待t1、t2

java 复制代码
public class Demo20 {

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(()->{
            for(int i = 0; i < 5; i++ ){
                System.out.println("这是t1线程");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }

            System.out.println("t1线程执行结束");
        });

        Thread t2 = new Thread(()->{
           for(int i = 0; i < 3; i++){
               System.out.println("这是t2线程");

               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }

           }

            try {
                //t2线程 等待 t1线程
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("t2线程结束");
        });


        t1.start();
        t2.start();
        System.out.println("main线程开始等待");
        t1.join();
        t2.join();
        System.out.println("main线程等待结束");


    }

}

我们来观察以下代码,先不考虑t1.join(),那么在t1,t2线程同时创建,同时执行逻辑时,那么应该t2线程先结束。因为 t1 要打印5次,而 t2 只需打印3次

t1,t2线程创建之后,和主线程一起同时执行逻辑,但由于主线程中的 t1.join,t2.join ,那么主线程先等待t1,t2线程执行逻辑。然后当 t2 打印三次之后又遇到了 t1.join,那么t2 开始等待 t1。所以出现了 t1线程执行结束,t2 线程结束,main线程结束的顺序。

其他线程等待

获取当前线程引用

java 复制代码
public class Demo21 {
    public static void main(String[] args) {
        Thread t = Thread.currentThread();
        System.out.println(t.getName());
    }
}

这也说明了,main 主线程的存在

线程状态

既然说到线程状态,那么我们不得不说一下它的"爸爸"进程的进程状态

进程状态:

就绪:正在 cpu 上执行,或者随时可以去 cpu 上执行

阻塞:暂时不能参与 cpu 执行

线程的六种状态

  1. NEW

当前 Thread 对象存在,但没有分配线程(即还没调用start)

  1. TERMINATED

当前 Thread 对象存在,但线程已经结束

  1. RUNNABLE

就绪状态:正在 cpu 上运行 或 随时可以去 cpu 上执行

  1. BLOCKED

因为 锁 竞争,引起的阻塞

  1. TIMED_WAITING

有时间限制的线程等待

  1. WAITING

没有时间的线程等待

相关推荐
繁依Fanyi18 小时前
从初识到实战 | OpenTeleDB 安装迁移使用指南
开发语言·数据库·python
小罗和阿泽18 小时前
java 【多线程基础 一】线程概念
java·开发语言·jvm
悟空码字18 小时前
SpringBoot整合Zookeeper,实现分布式集群部署
java·zookeeper·springboot·编程技术·后端开发
橘颂TA18 小时前
线程池与线程安全:后端开发的 “性能 + 安全” 双维实践
java·开发语言·安全
bruce_哈哈哈18 小时前
go语言初认识
开发语言·后端·golang
色空大师18 小时前
服务打包包名设置
java·elasticsearch·maven·打包
xiaoyustudiowww18 小时前
fetch异步简单版本(Tomcat 9)
java·前端·tomcat
十五年专注C++开发18 小时前
VS2019编译的C++程序,在win10正常运行,在win7上Debug正常运行,Release运行报错0xC0000005,进不了main函数
开发语言·c++·报错c0x0000005