【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

目录
相关文章
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
330 9
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
179 3
【JavaEE】——多线程常用类
Callable的call方法,FutureTask类,ReentrantLock可重入锁和对比,Semaphore信号量(PV操作)CountDownLatch锁存器,
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
Java Go 调度
【JavaEE】——线程池大总结
线程数量问题解决方式,代码实现线程池,ThreadPoolExecutor(核心构造方法),参数的解释(面试:拒绝策略),Executors,工厂模式,工厂类
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
Java 调度
【JavaEE】——线程的安全问题和解决方式
【JavaEE】——线程的安全问题和解决方式。为什么多线程运行会有安全问题,解决线程安全问题的思路,synchronized关键字的运用,加锁机制,“锁竞争”,几个变式
|
Java API 调度
【JavaEE】——多线程(join阻塞,计算,引用,状态)
【JavaEE】——多线程,join,sleep引起的线程阻塞,多线程提升计算效率,如何获取线程的引用和状态
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获

热门文章

最新文章