【并发编程】享元模式

       📝个人主页:五敷有你      
 🔥系列专栏:并发编程
⛺️稳重求进,晒太阳

享元模式

简介

定义 英文名称:Flyweight pattern. 当需要重用数量有限的同一类对象时

        享元模式是一种结构型的设计模式。它的主要目的是通过共享对象来减少系统种对象的数量,其本质就是缓存共享对象,降低内存消耗。

        享元模式将需要重复使用的对象分为两个部分:内部状态和外部状态。

        内部状态是不会变化的,可以被多个对象共享,而外部状态会随着对象的使用而改变。比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。

体现

包装类

在JDK中 Boolean,Byte,Short,Integer,Long,Character 等包装类提供了 valueOf 方法,例如 Long 的valueOf 会缓存 -128~127 之间的 Long 对象,在这个范围之间会重用对象,大于这个范围,才会新建 Long 对象:

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

注意:

  • Byte,Short,Long缓存的范围都是-128~127
  • Character 缓存的范围是 0~127
  • Integer的默认值是-128~127
    • 最小值不能变
    • 但最大值可以通过调整虚拟机参数
      • -Djava.lang.Integer.IntegerCache.high 来改变
  • Boolean缓存了TRUE 和 FALSE

String 串池

        String类是final修饰的,以字面量的形式创建String变量时,JVM会在编译期间就把该字面量“hello”放到字符串常量池中,由Java程序启动的时候就已经加载到内存中了。这个字符串常量池的特点就是有且只有一份相同的字面量,如果有其它相同的字面量,JVM则返回这个字面量的引用,如果没有相同的字面量,则在字符串常量池创建这个字面量并返回它的引用。

BigDecimal BigInteger

注意之前的BigDecimal是线程安全的为什么之前还要使用AtomicReference呢,因为

之前虽然他是安全的,但是那个是三条指令,每条指令的原子性不能保证三条指令的原子性,所以加入Atomic来CAS操作‘

DIY

例如:一个线上商城应用,QPS 达到数千,如果每次都重新创建和关闭数据库连接,性能会受到极大影响。 这时预先创建好一批连接,放入连接池。一次请求到达后,从连接池获取连接,使用完毕后再还回连接池,这样既节约了连接的创建和关闭时间,也实现了连接的重用,不至于让庞大的连接数压垮数据库。

class Pool {
    // 1. 连接池大小
    private final int poolSize;
    // 2. 连接对象数组
    private Connection[] connections;
    // 3. 连接状态数组 0 表示空闲, 1 表示繁忙
    private AtomicIntegerArray states;
    // 4. 构造方法初始化
    public Pool(int poolSize) {
        this.poolSize = poolSize;
        this.connections = new Connection[poolSize];
        this.states = new AtomicIntegerArray(new int[poolSize]);
        for (int i = 0; i < poolSize; i++) {
            connections[i] = new MockConnection("连接" + (i+1));
        }
    }
    // 5. 借连接
    public Connection borrow() {while(true) {
        for (int i = 0; i < poolSize; i++) {
            // 获取空闲连接
            if(states.get(i) == 0) {
                if (states.compareAndSet(i, 0, 1)) {
                    log.debug("borrow {}", connections[i]);
                    return connections[i];
                }
            }
        }
        // 如果没有空闲连接,当前线程进入等待
        synchronized (this) {
            try {
                log.debug("wait...");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    }
    // 6. 归还连接
    public void free(Connection conn) {
        for (int i = 0; i < poolSize; i++) {
            if (connections[i] == conn) {
                states.set(i, 0);
                synchronized (this) {
                    log.debug("free {}", conn);
                    this.notifyAll();
                }
                break;
            }
        }
    }
}
class MockConnection implements Connection {
    // 实现略
}

使用连接池

Pool pool = new Pool(2);
for (int i = 0; i < 5; i++) {
    new Thread(() -> {
        Connection conn = pool.borrow();
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.free(conn);
    }).start();
}

        以上实现没有考虑:连接的动态增长与收缩连接保活(可用性检测)等待超时处理分布式 hash对于关系型数据库,有比较成熟的连接池实现,例如c3p0, druid等 对于更通用的对象池,可以考虑使用apachecommons pool,例如redis连接池可以参考jedis中关于连接池的实现

Final原理

public class TestFinal { 
    final int a = 20;
}

字节码

0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 20
7: putfield #2 // Field a:I
 <-- 写屏障
10: return

发现 final 变量的赋值也会通过 putfield 指令来完成,同样在这条指令之后也会加入写屏障,保证在其它线程读到它的值时不会出现为 0 的情况

无状态

在web阶段学习时,设计Servlet时为了保证线程安全,都会有这样的建议,不要为Servlet设置成员变量,这种没有成员变量的类是线程安全的。

因为成员变量保存的数据也可以称为状态信息。因此没有成员变量就称为无状态、

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/379677.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

吉他学习:右手拨弦方法,右手拨弦训练 左手按弦方法

第六课 右手拨弦方法https://m.lizhiweike.com/lecture2/29362775 第七课 右手拨弦训练https://m.lizhiweike.com/lecture2/29362708

【Redis】深入理解 Redis 常用数据类型源码及底层实现(3.详解String数据结构)

【Redis】深入理解 Redis 常用数据类型源码及底层实现&#xff08;1.结构与源码概述&#xff09;-CSDN博客 【Redis】深入理解 Redis 常用数据类型源码及底层实现(2.版本区别dictEntry & redisObject详解)-CSDN博客 紧接着前两篇的总体介绍&#xff0c;从这篇开始&#x…

Android 环境搭建

1、桥接工具安装 网站地址&#xff1a;AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 使用安装包&#xff1a; adb 查看当前链接成功的设备&#xff1a;adb devices 使用adb shell指令来进入到手机的后台&#xff1a;

dddddddddddddddddddd

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 磁盘满的本质分析 专栏&#xff1a;《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具…

什么是路由器公网IP?

路由器公网IP是指路由器在互联网上的唯一标识&#xff0c;用于区分不同的网络设备。在互联网连接中&#xff0c;每个设备都需要一个公网IP地址才能与外部网络进行通信。路由器公网IP的获取和使用对于网络连接和数据传输非常重要。 路由器公网IP的获取方式 通常&#xff0c;路由…

Spring第三天

一、AOP 1 AOP简介 问题导入 问题1&#xff1a;AOP的作用是什么&#xff1f; 问题2&#xff1a;连接点和切入点有什么区别&#xff0c;二者谁的范围大&#xff1f; 问题3&#xff1a;请描述什么是切面&#xff1f; 1.1 AOP简介和作用【理解】 AOP(Aspect Oriented Progra…

Qt网络编程-写一个简单的网络调试助手

环境 Windows&#xff1a;Qt5.15.2&#xff08;VS2022&#xff09; Linux&#xff1a;Qt5.12.12&#xff08;gcc) 源代码 TCP服务器 头文件&#xff1a; #ifndef TCPSERVERWIDGET_H #define TCPSERVERWIDGET_H #include <QWidget> namespace Ui { class TCPServerW…

单片机的省电模式及策略

目录 一、单片机省电的核心策略 二、单片机IO口的几种模式 三、单片机的掉电运行模式 &#xff08;1&#xff09; 浅谈cpu运行为什么会需要时钟&#xff1f; &#xff08;2&#xff09;STC15系列单片机内部可以配置时钟 &#xff08;3&#xff09;分频策略&#xff0c;降低…

ubuntu22.04 安装部署05:禁用默认显卡驱动

一、相关文章 ubuntu22.04安装部署03&#xff1a; 设置root密码-CSDN博客 《ubuntu22.04装部署01&#xff1a;禁用内核更新》 《ubuntu22.04装部署02&#xff1a;禁用显卡更新》 二、场景说明 Ubuntu22.04 默认显卡驱动&#xff0c;如果安装cuda&#xff0c;需要单独安装显…

什么是向量数据库?为什么向量数据库对LLM很重要?

由于我们目前生活在人工智能革命之中&#xff0c;重要的是要了解许多新应用程序都依赖于向量嵌入&#xff08;vector embedding&#xff09;。因此&#xff0c;有必要了解向量数据库以及它们对 LLM 的重要性。 我们首先定义向量嵌入。向量嵌入是一种携带语义信息的数据表示形式…

了解海外云手机的多种功能

随着社会的高度发展&#xff0c;海外云手机成为商家不可或缺的工具&#xff0c;为企业出海提供了便利的解决方案。然而&#xff0c;谈及海外云手机&#xff0c;很多人仍不了解其强大功能。究竟海外云手机有哪些功能&#xff0c;可以为我们做些什么呢&#xff1f; 由于国内电商竞…

树与二叉树---数据结构

树作为一种逻辑结构&#xff0c;同时也是一种分层结构&#xff0c;具有以下两个特点&#xff1a; 1&#xff09;树的根结点没有前驱&#xff0c;除根结点外的所有结点有 且只有一个前驱。 2&#xff09;树中所有结点可以有零个或多个后继。 树结点数据结构 满二叉树和完全二…

Vue3编写简单的App组件(二)

一、Vue3页面渲染基本流程 1、入口文件 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><link rel"icon" href"/favicon.ico"><meta name"viewport" content"widthde…

PlantUML绘制UML图教程

UML&#xff08;Unified Modeling Language&#xff09;是一种通用的建模语言&#xff0c;广泛用于软件开发中对系统进行可视化建模。PlantUML是一款强大的工具&#xff0c;通过简单的文本描述&#xff0c;能够生成UML图&#xff0c;包括类图、时序图、用例图等。PlantUML是一款…

【前端web入门第四天】01 复合选择器与伪类选择器

文章目录: 1. 复合选择器 1.1 后代选择器 1.2 子代选择器 1.3 并集选择器1.4 交集选择器(了解) 2.伪类选择器 2.1 伪类-文本2.2 伪类-超链接&#xff08;拓展) 1. 复合选择器 什么叫复合选择器? 由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 复合选择器的作…

Python中使用opencv-python库进行颜色检测

Python中使用opencv-python库进行颜色检测 之前写过一篇VC中使用OpenCV进行颜色检测的博文&#xff0c;当然使用opencv-python库也可以实现。 在Python中使用opencv-python库进行颜色检测非常简单&#xff0c;首选读取一张彩色图像&#xff0c;并调用函数imgHSV cv2.cvtColor…

从REPR设计模式看 .NET的新生代类库FastEndpoints的威力

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 又到了一年年末,春节将至…

Android SDK 上传 Maven 喂奶级教程

最近领导给安排了个任务&#xff0c;让我把我们现有的一个 SDK 上传到 Maven 上去&#xff0c;方便客户直接用 gradle 依赖&#xff0c;不再需要拷贝 jar 和 so 了&#xff0c;此前我也看过一些相关的文章我想问题也不大&#xff0c;觉得工作量也就一两天的事情&#xff0c;主要…

深度学习在知识图谱问答中的革新与挑战

目录 前言1 背景知识2 基于深度学习改进问句解析模型2.1 谓词匹配2.2 问句解析2.3 逐步生成查询图 3 基于深度学习的端到端模型3.1 端到端框架3.2 简单嵌入技术 4 优势4.1 深入的问题表示4.2 实体关系表示深挖4.3 候选答案排序效果好 5 挑战5.1 依赖大量训练语料5.2 推理类问句…

【原创 附源码】Flutter海外登录--Tiktok登录最详细流程

最近接触了几个海外登录的平台&#xff0c;踩了很多坑&#xff0c;也总结了很多东西&#xff0c;决定记录下来给路过的兄弟坐个参考&#xff0c;也留着以后留着回顾。更新时间为2024年2月7日&#xff0c;后续集成方式可能会有变动&#xff0c;所以目前的集成流程仅供参考&#…