【JavaEE】wait and notify-线程状态总结

简介: JavaEE & wait and notify & 线程状态总结

JavaEE & wait and notify & 线程状态总结

1. wait and notify

学到现在,我们已经了解到了阻塞的很多状况

阻塞的本质就是,纠正线程无序调度,让线程在某些需求场景下,有序进行

join,等待整个线程结束/等待一段时间

sleep,线程固定睡眠

synchronized,等待锁

上面的三种,各有各的好,功效有限

而wait呢,它也有它的特性

它引起的阻塞等待,就相当于给线程按了暂停键

而notify呢,则是让线程继续执行

而这个notify应该是阻塞线程外的一个线程调用的

什么时机按下启动键,看具体需要~

而这两个方法进行配合,可以更精细的控制线程调度顺序~

根据 【适用场景:::我们的初心】 去选择阻塞方式~


1.1 应用场景

假设你今天要去银行取钱,ATM没钱了

而其他人都有钱

假设那么ATM是个锁对象

那么你跟其他人一起抢这个锁去操作

而你是要取钱的,如果没人存钱或者没有工作人员运钞填入钱,你就不能取到钱

尽管你抢到锁也没用~

那么,如果你在一个很短的时间内,反复抢到锁,那么是不是就是在浪费时间

这个时间很短,线程的记账信息还没起作用~

而接下来,我们就是为了解决这个问题~

755d8ecc58944725a2987dad94757fb6.gif


1.2 wait与notify配合解决问题

基本思想就是

你抢到ATM机了,你取不到钱,你就wait,直到别人来notify你说,你可以去取钱了~

1.2.1 wait和notify的使用

wait和notify都是Object类的方法,只要不是基本数据类型的变量,都可以调用~

谁调用wait,则对应该线程就会阻塞等待

谁调用notify,无所谓,随机唤醒一个WAITING的线程

9ea6c57c660943bd9ea34d0a3def39bb.png


简简单单的调用是会报错的~

c86126188a1d4322b50296c941de0430.png


这里也体现了wait的主要要做的三件事:

释放锁

阻塞等待

等待通知唤醒,唤醒后继续参与“抢锁”

补充: notify能够唤醒的线程,必须和notify的锁对象是一样的

notify要依据这个锁去唤醒线程~

没有抢到锁,就notify一样会报错

并且notify也有一步操作是,释放锁,否则被唤醒的线程和其他线程无法去抢夺锁

3aae67414d0f4d1488c52bac93d6093f.png


1.2.2 wait和notify代码规范

必须先wait再notify才有效果,否则就没有效果

将相当于你取得到钱,你也没wait阻塞等待,就有人来notify你说,可以取钱去了,你会觉得莫名其妙,但是对你没有什么影响

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        Thread thread1 = new Thread(() -> {
            while(true) {
                System.out.println("wait 前");
                synchronized (object) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("wait 后");
            }
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("notify 开始");
            synchronized (object) {
                object.notify();
            }
            System.out.println("notify 结束");
        });
        thread1.start();
        thread2.start();
    }
}

测试结果:


ce7f39df0d8445eaa5e2063c1a2e587b.png

顺序正常~

这就是通过具体情况,调整cpu调度顺序的方式~

很多方法都能引起这种效果,但是初心不同,所以,要看具体想法,使用特定的方式~


1.2.3 补充

wait还有一个重载方法,提供了带参数的方法

这个参数代表了,最长等待时间,超过该时间,就自动唤醒~

befb2b38ef7e4eef9b172b33c0d16a3a.png


notify还有一个哥哥,notifyAll

他可以根据自己的锁对象,去唤醒所有对应的线程~

而notify是随机唤醒多个中的一个~


0761306385ef4ff4b84c4810d678d1e8.png

sleep,join好像也能被中断唤醒,也有时间限制,这样就跟wait差不多了咯~


没错,这个场景差不多,但是他们的初心不一样

在不同场景用更加有针对性的方式,才是好决策~

目前的场景只是为了体现wait和notify的使用方式


你可能没能体会到,他们的配合的在这里对任务的针对性~


并且,锁代码块也并不是只有那么个语句~

以后写的应该是特定情况下会wait~

没有关系,后面我会写几篇博客,研究 Java多线程的一些案例


综合线程知识去研究

结合具体场景运用不同方式

2. 线程状态总结


2551e0042ffb4b72ab74d0e215a0fbf8.png

在之前的《进程与线程》博客里给出这张图,由于部分知识未学习,现在重新看一遍~

接下来,我将讲解各个状态,并且用代码去测试

2.1 NEW

NEW 线程还没被创建出来,只是存在这个线程对象~


start 创建 + 启动

也就是说start之前~

public class TestState {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
        });
        System.out.println(thread1.getState());
        thread1.start();
    }
}


bd6cd203e8f349048c2228b05dd72f93.png

2.2 RUNNABLE

RUNNABLE 运行/就绪状态


正在CPU上运行准备好

随时可以去CPU运行

这两种情况,基本“同时”

因为线程并发执行的原因

所以一个线程总是处于运行和就绪的状态~

但是这一个很小的时间内,并不是阻塞~

public class TestState {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
        });
        thread1.start();
        System.out.println(thread1.getState());
    }
}


fe80f83becd84290b898969efb94100e.png


2.3 TERMINATED

TERMINATED 系统中的线程已经执行完毕~


但是线程引用还在~

通过这个“还在的”引用去查看状态

由于线程调度的随机性,所以2.2代码是可能出现TERMINATED状态的,如下图:


但是概率低~


8a1c5f4c797e4265a42ff3ec11794df1.png

所以我让main线程等一会儿~


public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
        });
        thread1.start();
        Thread.sleep(10);
        System.out.println(thread1.getState());
    }
}


2.4 TIMED_WAITING

TIMED_WAITING


被指定时间的等待—sleep

对于join方法

即使正在等待的线程是完全阻塞的状态

我们可以通过全局性质的静态变量去获得线程引用并在lambda表达式中被捕获到~

如果有时间限制就是TIMED_WAITING~

否则是WAITING~

public class TestState {
    public static Thread t;
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
            System.out.println(t.getState());
        });
        thread1.start();
        t = Thread.currentThread();
        thread1.join(1000);
    }
}

d7795215af8e49998916d4005e038d7f.png


public class TestState {
    public static Thread t;
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
            System.out.println(t.getState());
        });
        thread1.start();
        t = Thread.currentThread();
        Thread.sleep(1000);
    }
}

d7795215af8e49998916d4005e038d7f.png



public class TestState {
    public static Thread t;
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("好耶 ^ V ^");
            System.out.println(t.getState());
        });
        thread1.start();
        t = Thread.currentThread();
        thread1.join();
    }
}


549ef042d3bb43d0b0571f829c0996d6.png

2.5 BLOCKED

BLOCKED


表示等待锁出现的状态

class Counter {
    private int count = 0;
    public void add() {
        synchronized (Counter.class) {
            count++;
        }
    }
    public int get() {
        return count;
    }
}

cc31ac52ba474aad85be13a5bb3b3a82.png

public class TestState {
    public static Thread t;
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (counter) {
                    counter.add();
                    System.out.println(t.getState());
                }
            }
        });
        thread1.start();
        t = Thread.currentThread();
        for (int i = 0; i < 5000; i++) {
            synchronized (counter) {
                counter.add();
            }
        }
    }
}


而main线程可能很快就会执行完,所以就没必要阻塞等待了~

efb5fec6e22a442685b2c482fae5b6cf.png


2.6 WAITING

主要是因为wait方法

join()不带参数版本,是等待整个线程结束

很直接

下面在线程2notify前后分别去查看线程1的状态~

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        Thread thread1 = new Thread(() -> {
            while(true) {
                System.out.println("wait 前");
                synchronized (object) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("wait 后");
            }
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("notify 开始");
            System.out.println(thread1.getState());
            synchronized (object) {
                System.out.println(thread1.getState());
                object.notify();
            }
            System.out.println("notify 结束");
            System.out.println(thread1.getState());
        });
        thread1.start();
        thread2.start();
    }
}

f8d8ac5e35ae417b8cacceb62e79bfd7.png

目录
相关文章
|
2天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
9 3
|
17天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
13 1
|
17天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
22 1
|
17天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
28 1
|
17天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
22 1
|
1月前
|
调度
【多线程-从零开始-陆】wait、notify和notifyAll
【多线程-从零开始-陆】wait、notify和notifyAll
22 0
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
39 1
C++ 多线程之初识多线程
|
17天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
14 3
|
17天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
13 2