【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,还未完全初始化的对象,那么此时就出现错误!!!

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

相关推荐
橘猫云计算机设计25 分钟前
基于微信小程序的疫情互助平台(源码+lw+部署文档+讲解),源码可白嫖!
java·大数据·开发语言·spring boot·微信小程序·小程序·汽车
m0_6724496031 分钟前
使用Java操作Excel
java·python·excel
customer0843 分钟前
【开源免费】基于SpringBoot+Vue.JS医院药品管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
无世世1 小时前
【Java从入门到起飞】面向对象编程(基础)
java·开发语言
Code哈哈笑1 小时前
【Java EE】JavaEE导读,探寻 JavaEE:解锁企业级开发的璀璨密码与进阶指南
java·java-ee
刘小炮吖i2 小时前
Java自动拆箱装箱/实例化顺序/缓存使用/原理/实例
java·缓存·面试
节点。csn2 小时前
java 项目中设计模式 之单例模式
java·单例模式·设计模式
路在脚下@2 小时前
门面设计模式和适配器模式有什么区别
java
土豆炒马铃薯。2 小时前
【Java 基础(人话版)】Java 虚拟机(JVM)
java·开发语言·jvm·后端·java基础·虚拟机
怪咖码农2 小时前
RabbitMQ怎么实现延时支付?
java·分布式·rabbitmq