【JavaEE】wait 、notify和单例模式

【JavaEE】wait 、notify 和单例模式

博客结尾有此篇博客的全部代码!!!

一、引言

假设这里有一个ATM机,然后有人排队取钱,有人排队存钱

ATM机时进去一个人,门自动关门上锁。

现实生活中,假设第一个人进去取钱,但是恰好此时ATM机里面没有钱,第一个人就出来,刚出来他又觉得ATM机里面有钱了(有押运车来往里放 钱),又进去了,他这样来回反复横跳,一直占着ATM机,不让第二个和第三个人使用!

在操作系统中,这种情况是经常发生的,因为资源的调度是随机的。假设这里有多个线程,系统一直调用某一个线程,让其他线程一直处于阻塞等待,这种情况就叫做线程饿死(线程饥饿)

那么怎么解决这种问题呢?这里就引出了wait()方法!!!

一、wait()方法

  • wait() 方法用于使当前线程等待(挂起)(释放当前锁),让其他线程可以使用这把锁。
java 复制代码
    public static void main(String[] args) {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("wait 之前");
                    lock.wait();
                    System.out.println("wait 之后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
    }

由于这段代码在执行wait()方法之后就一直等待下去,这里肯定不可能让这段代码一直等待下去。所以就引入一个新的方法唤醒wait()方法------notify()方法。

wait()方法需要注意的:

  1. 必须搭配synchronized()使用,脱离synchronized()就会抛出异常
  2. 其他线程调⽤该等待线程的 interrupted ⽅法, 导致 wait 抛出 InterruptedException 异常
  3. wait()方法也有等待超时

二、notify()方法

这个方法是为了唤醒wait()方法,防止wait()方法一直等待下去!

• ⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

• 如果有多个线程等待,则有线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 "先来后到")

• 在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

java 复制代码
public class Demo2 {
    public static void main(String[] args) {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("wait 之前");
                    lock.wait();
                    System.out.println("wait 之后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                lock.notify();
                System.out.println("唤醒wait()方法");
            }
        });
        t1.start();
        t2.start();
    }
}

如果这里有多个wait()方法,只有一个notify()方法,这个唤醒是随机的!!!

三、notifyAll()方法

notify⽅法只是唤醒某⼀个等待线程. 使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程.

java 复制代码
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        Thread t3 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("t3 wait()方法之前");
                    lock1.wait();
                    System.out.println("t3 wait()方法之后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("t1 wait()方法之前");
                    lock1.wait();
                    System.out.println("t1 wait()方法之后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });


        Thread t2 = new Thread(() -> {
            synchronized (lock1) {
                lock1.notifyAll();
                System.out.println("唤醒wait()方法");
            }
        });
        t3.start();
        t1.start();
        t2.start();
    }
}

wait()、notify()、notifuAll()三种方法都是Object类中的方法!!!

四、wait()和sleep()对比

相同:

  • 都可以让线程放弃一段时间。

不同:

wait:

  • 是Object类中的方法
  • 需要搭配synchronized使用
  • 用于线程间的协作
  • 释放锁,允许其他线程运行
  • 需要在同步块中调用
  • 可以通过 notify() 或超时唤醒

sleep:

  • 是Thread的静态方法
  • 用于简单的延时操作
  • 不释放锁
  • 可以在任何地方调用
  • 只能通过睡眠时间结束或中断唤醒

五、单例模式

设计模式(Design Patterns)是软件工程中用于解决常见问题的可复用的解决方案。它们是经过验证的最佳实践,能够帮助开发者设计出更灵活、可维护和可扩展的代码。

单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建多个实例。

Thread t1=new Thread();t1就是创建的实例。

单例模式具体的实现方式有很多,最常见的是"饿汉"和"懒汉"两种。

5.1 饿汉模式

类加载的同时,创建实例

java 复制代码
class MyDesign{
    private static MyDesign obj = new MyDesign();
    private MyDesign(){}
    public static MyDesign getInstance(){
        return obj;
    }
}

5.2 懒汉模式

类加载的时候不创建实例. 第⼀次使⽤的时候才创建实例.

java 复制代码
class MyDesignF{
    private static MyDesignF obj = null;
    private MyDesignF(){}
    public static MyDesignF getInstance(){
        if(obj == null){
            obj = new MyDesignF();
        }
        return obj;
    }
}

饿汉模式不会出现线程安全问题;懒汉模式会出现线程安全问题!

饿汉模式 :没有修改操作,所以不会出现线程安全问题。
懒汉模式:有修改操作,所以线程不安全。

5.2 懒汉模式-线程安全(改进)

  • 加synchronized
java 复制代码
class MyDesignF{
    private static MyDesignF obj = null;
    private MyDesignF(){}
    public  static MyDesignF getInstance(){
        synchronized (MyDesignF.class){
            if(obj == null){
                obj = new MyDesignF();
            }
        }
        return obj;
    }
}
  • 双重if判断,降低锁竞争的频率
java 复制代码
class MyDesignF{
    private static MyDesignF obj = null;
    private MyDesignF(){}
    public  static MyDesignF getInstance(){
        if(obj == null){
            synchronized (MyDesignF.class){
                if(obj == null){
                    obj = new MyDesignF();
                }
            }
        }
        return obj;
    }
}
  • 给obj加上volatile
java 复制代码
class MyDesignF{
    private volatile static MyDesignF obj = null;
    private MyDesignF(){}
    public  static MyDesignF getInstance(){
        if(obj == null){
            synchronized (MyDesignF.class){
                if(obj == null){
                    obj = new MyDesignF();
                }
            }
        }
        return obj;
    }
}

volatile关键字作用:(防止指令重排序

在懒汉模式中,obj = new MyDesignF();也可以分为三步:

  • 分配内存空间。----1
  • 调用构造函数初始化对象。----2
  • 将内存地址赋值给 obj 变量。----3

正常情况下是1-》2-》3,但如果发生指令重排序就会出现1-》3-》2,先将内存地址赋值给obj,但还没有进行初始化对象,此时如果另一个线程访问obj,还未完全初始化的对象,那么此时就出现错误!!!

此篇博客的全部代码!!!

相关推荐
SoFlu软件机器人24 分钟前
AI 重构 Java 遗留系统:从静态方法到 Spring Bean 注入的自动化升级
java·spring·重构
liwulin050626 分钟前
【JAVA】JVM 堆内存“缓冲空间”的压缩机制及调整方法
java·开发语言·jvm
程序员小假1 小时前
十个JVM核心知识点【全文万字保姆级详细讲解】
java·后端
泉城老铁1 小时前
springboot对接钉钉,发送钉钉消息
java·前端·后端
鸟语滑翔1 小时前
【学生管理系统升级版】
java
佩奇的技术笔记1 小时前
Java学习手册:JVM、JRE和JDK的关系
java·开发语言·jvm
夔8881 小时前
Excel通过VBA脚本去除重复数据行并保存
java·服务器·excel
AronTing1 小时前
13-Java并发编程性能优化终极指南:从原理到企业级实战
java·后端·面试
布道谷1 小时前
锁的可重入性:概念、原理与Java实现深度解析
java·后端
雷渊1 小时前
谈一谈在分布式系统中,如何保证数据一致性?
java·后端·面试