当前位置: 首页 > article >正文

【JavaEE初阶】多线程(5 单例模式 \ 阻塞队列)

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~



目录

实例1: 单例模式

饿汉模式

懒汉模式

实例2:阻塞队列

生产者消费者模型 

优点

​编辑 代价

简单实现一个生产者消费者模型 

Java标准库中的阻塞队列

​编辑 模拟实现一个阻塞队列


实例1: 单例模式

单例模式 是一种设计模式,(固定套路,针对一些特定的场景,给出的一些比较好的解决方案)

开发中,希望有的类在一个进程中,不应该存在多个实例(对象),此时就可以使用单例模式(单个实例/对象),限制某个类只能有唯一实例, 比如 一般来说,一个程序 只有一个数据库,对应的mysql服务器只有一份,此时DataSource这个类就没有必要创建出多个实例了,此时使用单例模式描述DataSource,避免不小心创建出多个实例

Java中单例模式的实现有很多种,下面介绍两种最主流的写法: 饿汉模式 和 懒汉模式

饿汉模式

程序启动,在类被加载的时候, 就会创建出这个单例的实例, 不涉及线程安全问题

// 创建一个单例的类
// 饿汉方式实现.
// 饿 的意思是 "迫切"
// 
class Singleton{
    private static Singleton instance =new Singleton();

    public static Singleton getInstance(){
        return instance;
    }

    //单例模式的 最关键部分
    private Singleton(){

    }
}

 

单例模式只能避免别人"失误",无法应对别人的"故意攻击"(可以通过 反射 和 序列化反序列化打破上述单例模式)

懒汉模式

推迟了创建实例的时机,在程序第一次使用这个实例的时候 才会创建实例 ,涉及线程安全问题

class SingletonLazy{
    //此处先把实例设为null,先不着急创建实例
    private static volatile SingletonLazy instance =null;
    private static Object locker =new Object();
    public static SingletonLazy getInstance(){
        if(instance==null){
            synchronized(locker){
                if(instance==null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy(){ }
}

可能存在的线程安全问题

通过加锁解决,将if判定和new赋值操作,打包成原子操作  

 

双重if 判定 , 通过if条件判定是否要加锁 

编译器优化(指令重排序) 造成的线程安全问题

在执行3)步骤之后并且未执行2)步骤时 如果线程发生切换,此时会直接返回instance,但是这个被返回的对象是没有被初始化的,由于该对象未初始化,一旦调用该对象里的成员,都可能是错误的值,引起一系列不可预期的情况.

解决方法:在instace的前面加上volatile.

实例2:阻塞队列

阻塞队列 是在普通队列(先进先出)的基础上做出扩充:

  • 线程安全  (标准库中原有的队列Queue和其子类,默认都是线程不安全的)
  • 具有阻塞特性  
    • 如果队列为,进行出队列操作 就会出现阻塞,一直阻塞到其他线程往队列里添加元素为止
    • 如果队列为,进行入队列操作 也会出现阻塞,一直阻塞到其他线程从队列中取走元素为止

生产者消费者模型 

该模型是基于阻塞队列的最大应用场景

优点

使用该模型的优点:

1,有利于服务器之间的"解耦合"

 2,通过中间的阻塞队列,可以起到"削峰填谷"的效果(在遇到请求量激增的情况下,可以有效保护下游服务器,不会被请求冲垮)

通常谈到的"阻塞队列"是代码中的一个数据结构,但是由于这个东西太好用了,以至于被单独封装成了一个服务器程序,并且在单独的服务器机器上部署,此时这样的阻塞队列有了一个新的名字:"消息队列"(Message Queue ,MQ) 

 代价

  • 需要更多的机器 来部署这个消息队列
  • A和B之间的通信延时 会变长

简单实现一个生产者消费者模型 

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Demo27 {
    public static void main(String[] args) {
        // BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1000);
        MyBlockingQueue queue = new MyBlockingQueue(1000);

        // 生产者线程
        Thread t1 = new Thread(() -> {
            int i = 1;
            while (true) {
                try {
                    queue.put("" + i);
                    System.out.println("生产元素 " + i);
                    i++;

                    // 给生产操作, 加上 sleep, 生产慢点, 消费快点
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    Integer i = Integer.parseInt(queue.take());
                    System.out.println("消费元素 " + i);

                    // 给消费操作, 加上 sleep, 生产快点, 消费慢点
                    // Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

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

Java标准库中的阻塞队列

入队列产生阻塞效果,代码演示

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Demo18 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue=new ArrayBlockingQueue<>(3);
        queue.put("111");
        System.out.println("put成功");
        queue.put("111");
        System.out.println("put成功");
        queue.put("111");
        System.out.println("put成功");
        queue.put("111");
        System.out.println("put成功");
    }
}

 模拟实现一个阻塞队列

 基于数组实现的一个阻塞队列


class MyBlockingQueue{
    private String[] data=null;
    private volatile int head =0;
    private volatile int tail =0;
    private volatile int size =0;

    public MyBlockingQueue(int capacity){
        data = new String[capacity];
    }

    public void put(String s) throws InterruptedException {
        synchronized (this){
            //下面有大量的修改操作,加上锁保证线程安全
            //可以单独定义一个锁对象,也可以使用this表示当前对象
            while(size == data.length){
                //队列满了
//                return;
                this.wait();
            }
            data[tail]=s;
            tail++;
            if(tail >= data.length){
                tail=0;//如果tail移到了末尾,直接让tail到0位置,构成循环队列
            }
            size++;

            this.notify();
        }
    }

    public String take() throws InterruptedException {
        String ret= "";
        synchronized (this){
            while(size == 0){
//                return null;
                this.wait();
            }
             ret = data[head];

            head++;
            if(head>=data.length){
                head = 0;
            }
            size--;

            this.notify();
        }
        return ret;
    }
}

注意:要将使用wait时的if判断换成while循环,使代码更加稳健

while 的作用,就是在wait被唤醒之后再次确认条件,看是否能继续执行


http://www.kler.cn/a/308549.html

相关文章:

  • 【CSS】“flex: 1“有什么用?
  • 定时器(QTimer)与随机数生成器(QRandomGenerator)的应用实践——Qt(C++)
  • Apache ECharts
  • windows C#-LINQ概述
  • python数据写入excel文件
  • ESLint 使用教程(七):ESLint还能校验JSON文件内容?
  • 构建基于 Feign 的微服务:从 Eureka 到负载均衡的实践 --day05
  • 微信支付开发-前端api实现
  • 大模型笔记03--快速体验dify
  • HTTP的强制缓存和协商缓存有什么区别和联系?
  • 《使用 LangChain 进行大模型应用开发》学习笔记(三)
  • 行人动作行为识别系统源码分享
  • LLamaindex基本使用
  • MYSQL数据库基础篇——DDL
  • 第7篇:【系统分析师】计算机网络
  • openwrt wsdd模块介绍
  • C++(重载)
  • Skytower
  • 第二百三十五节 JPA教程 - JPA Lob列示例
  • k8s(kubernetes)的PV / PVC / StorageClass(理论+实践)
  • 数据库的操作:SQL运算符(算法/比较/逻辑/位)
  • 【大模型专栏—进阶篇】智能对话全总结
  • 力扣100题——动态规划(二)
  • React Native防止重复点击
  • 详解 Pandas 的透视表函数
  • PHP智能化云端培训考试系统小程序源码