JavaEE 初阶篇-深入了解多线程等待与多线程状态

发布于:2024-03-29 ⋅ 阅读:(26) ⋅ 点赞:(0)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 线程等待

        1.1 线程等待 - join() 方法

        1.1.1 main 线程中等待多个线程

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        1.1.3 其他线程阻塞等待 main 线程

        1.1.4 在指定的时间内阻塞等待

        1.2 线程等待 - Thread.sleep() 方法

        2.0 线程状态

        2.1 新建状态 - NEW

        2.2 就绪状态 - Runnable

        2.3 终止状态 -Terminated

        2.4 等待状态 - Waiting

        2.5 超时等待状态 - Time_Waiting

        2.6 阻塞状态 - Blocked

        2.7 线程状态之间的相互转换图


        1.0 线程等待

        在线程编程中,线程等待是指一个线程暂停执行,直到某个条件满足或者其他线程执行完毕后再继续执行。线程等待的方法:join() 方法Thread.sleep() 方法等等

        1.1 线程等待 - join() 方法

       join() 方法是 Thread 类的一个方法,用于让一个线程等待另一个线程执行完毕。当在一个线程对象上调用 join() 方法时,当前线程会被阻塞,直到被调用的线程执行完毕。

        具体来说,调用 thread.join() 会使当前线程等待 thread 线程执行完毕。如果 thread 线程已经执行完毕,那么 join() 方法会立即返回;如果 thread 线程还在执行,当前线程会被阻塞,直到 thread 线程执行完毕。

代码如下:

public class demo1 {


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

        Thread thread = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                System.out.println("正在执行 thread 线程");
            }
        });

        thread.start();
        thread.join();
        System.out.println("执行 main 线程");

    }
}

        main 线程调用 thread.join() ,就会阻塞 main 线程继续执行,会让 thread 线程执行完毕之后,main 线程解除阻塞,继续执行下去。

运行结果:

补充: main 中调用 join() 方法,有以下可能性:

        1.0 如果 t 线程此时已经结束了,此时 join() 方法就会立即返回。

        2.0 如果 t 线程此时还没有结束,此时 main 就会阻塞等待,一直等待到 t 线程结束之后,main 线程才会接触阻塞,继续执行。

        3.0 如果调用等待阻塞的线程对象还没创建 pcb 的时候(即还没 start() 的时候),那么调用 join() 方法的线程会直接解除阻塞。

        1.1.1 main 线程中等待多个线程

        在 main 线程中多次调用 join() 方法时,先执行 t1.join(),如果 t1 还没结束,main 继续阻塞等待,t1 结束之后,继续执行 t2.join() 方法,再等待 t2 结束。注意,t1 与 t2 之间同样是抢占式执行随机调度,所以先后顺序对于 main 线程来说没有什么区别。

代码如下:

public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t2 线程");
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("正在执行 main 线程");
    }
}

运行结果:

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        这种情况是按顺序执行的,大致就是串行执行一样。顺序为:先要执行完 t1 再执行 t2 最后再执行 main 线程。

代码如下:

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


        Thread t1 = new Thread(()->{

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }
        });

        Thread t2 = new Thread(()->{

            try {
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t2 线程");
            }

        });

        t1.start();
        t2.start();

        t2.join();
        System.out.println("执行 main 线程");
    }
}

        t1.start() 立即创建 t1 线程,再 t2.start() 创建线程,t1 没有任何阻塞就会直接执行代码,而 t2 遇到了阻塞,需要等待 t1 执行完毕之后,t2 才会解除阻塞,同时由于 main 线程阻塞了,需要等待 t2 执行完毕,当 t2 执行完毕之后,main 线程解除阻塞了,执行 main 线程中的代码。

运行结果:

        1.1.3 其他线程阻塞等待 main 线程

        t1 线程阻塞等待 main 线程执行完毕之后,再执行 t1 线程。

代码如下:

public class demo4 {

    public static void main(String[] args) throws InterruptedException {
        //拿到当前 main 线程对象
        Thread mainThread = Thread.currentThread();

        Thread t1 = new Thread(()->{

            //在 t1 线程中调用 join() 方法,
            //阻塞当前 t1 线程,等待 main 线程执行完毕
            try {
                mainThread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }

        });
        t1.start();

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("正在执行 main 线程");
        }

    }
}

运行结果:

        1.1.4 在指定的时间内阻塞等待

        join() 方法还有一个重载的版本,可以指定一个超时时间,即 join(long millis),表示当前线程最多等待 millis 毫秒,如果超过这个时间 thread 线程还没有执行完毕,当前线程会继续往下执行。简单来说,即使 thread 这个线程还没有结束,main 线程都不会继续等待了;如果 thread 在规定的时间内提前结束,那么 main 也会提前解除阻塞。

代码如下:

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

        Thread thread = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                System.out.println("正在执行 thread 线程");
            }
        });

        thread.start();

        //等待 0.1 s 就解除阻塞 main 线程,即不会继续等待了
        thread.join(1000);
        System.out.println("正在执行 main");
    }
}

运行结果:

        1.2 线程等待 - Thread.sleep() 方法

        是一个静态方法,它使当前线程暂停执行一段时间。该方法接受一个以毫秒为单位的时间参数,指定线程暂停的时间长度。在这段时间内,线程不会执行任何操作,但是线程的状态仍然是 Runnable 状态,可以随时被调度器调度执行。

        需要注意的是,Thread.sleep() 方法不是真正意义上的线程等待,它只是让线程暂停执行一段时间,不会释放锁或资源。在实际开发中,应根据具体需求选择合适的线程等待机制,以确保程序的正确性和效率。

代码如下:

public class demo6 {
    public static void main(String[] args) {

        Thread thread = new Thread(()->{

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 thread 线程");
            }
        });
        thread.start();
        
    }
}

运行结果:

        每相隔 1 秒就会输出一次。

        需要注意的是,这里的 Thread.sleep() 方法是受查异常,且 run() 是重写父类的方法,该 run() 方法的方法名、参数列表、声明异常都需要与父类保持一致,因此这里不能声明异常,只能捕获异常处理。

        2.0 线程状态

        在 Java 中主要包括六种不同的状态。

        2.1 新建状态 - NEW

        当创建一个线程对象时,线程处于新建状态,此时线程对象已经创建好了,但是还没调用 start() 方法启动线程,因此线程还没被创建出来

        2.2 就绪状态 - Runnable

        有两种情况都属于就绪状态:1)还没运行,就绪状态。但是线程已经准备好运行了,只等待被 CPU 调度执行。2)线程正在被 CPU 调度执行中,运行状态。总而言之,无论是就绪状态还是运行状态在 Java 中都属于 Runnable 状态,即就绪状态。

        2.3 终止状态 -Terminated

        线程执行完任务后或者出现异常导致线程终止时,线程进入终止状态。在终止状态下,线程不再执行任务。

        2.4 等待状态 - Waiting

        线程进入等待状态通常时因为调用了 thread.join() 方法等等。

代码如下:

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

        Thread thread = new Thread(()->{
            while (true){
                System.out.println(1);
            }
        });
        thread.start();

        thread.join();
        System.out.println("正在执行 main 线程");
    }
}

演示线程等待:

        main 线程调用了 thread.join() 方法阻塞等待 thread 线程,又因为 thread 还当前为止还没结束, 所以当前 main 线程被阻塞了,因此 main 状态为 Waiting 状态。对于 thread 线程来说,目前的状态为 Runnable 状态。

        2.5 超时等待状态 - Time_Waiting

        线程调用带有超时参数的等待方法,比如 Thread.sleep(long millis) 等待方法。线程会进入超时等待状态下,线程会等待一段时间后自动恢复到就绪状态。

代码如下:

public class demo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(9000000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("正在执行 thread 线程");
        });
        thread.start();

        System.out.println("正在执行 main 线程");
    }
}

演示超时等待:

        2.6 阻塞状态 - Blocked

        线程在特定情况下,会进入阻塞状态,比如调用了 Thread.sleep() 方法或者加锁。在线程阻塞状态下,线程暂时停止执行,直到满足特定条件后,才能继续执行。

代码如下:

死锁状态:两个线程两把锁

public class demo10 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        Thread t1 = new Thread(()->{
           synchronized (o1){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (o2){
                   System.out.println("正在执行 t1 线程");
               }
           }
        });


        Thread t2 = new Thread(()->{
           synchronized (o2){

               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (o1){
                   System.out.println("正在执行 t2 线程");
               }
           }
        });

        t1.start();
        t2.start();
    }
}

演示阻塞状态:

        2.7 线程状态之间的相互转换图

本文含有隐藏内容,请 开通VIP 后查看