共享模型之不可变——不可变设计、享元模式

文章目录

  • 不可变设计
    • final 的使用
    • 保护性拷贝
  • 享元模式
    • 简介
    • 体现
      • 包装类
      • String 串池
      • BigDecimal BigInteger
    • DIY

不可变设计

另一个大家更为熟悉的 String 类也是不可变的,以它为例,说明一下不可变设计的要素

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0// ...}

其中第一个final保证了char的数组的引用不可被更改,第二个hash,是私有的,并且没有set方法,因此这些成员变量都是不可变的。
另外一点,String类上也加了final字符,这就防止了子类的破坏

final 的使用

发现该类、类中所有属性都是 final 的

  • 属性用 final 修饰保证了该属性是只读的,不能修改
  • 类用 final 修饰保证了该类中的方法不能被覆盖,防止子类无意间破坏不可变性

保护性拷贝

但有同学会说,使用字符串时,也有一些跟修改相关的方法啊,比如 substring 等,那么下面就看一看这些方法是如何实现的,就以 substring 为例:

public String substring(int beginIndex) {if (beginIndex < 0) {throw new StringIndexOutOfBoundsException(beginIndex);}int subLen = value.length - beginIndex;if (subLen < 0) {throw new StringIndexOutOfBoundsException(subLen);}return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

发现其内部是调用 String 的构造方法创建了一个新字符串,再进入这个构造看看,是否对 final char[] value 做出了修改:

public String(char value[], int offset, int count) {
if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {if (count < 0) {throw new StringIndexOutOfBoundsException(count);}if (offset <= value.length) {this.value = "".value;return;}
}
if (offset > value.length - count) {throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}

结果发现也没有,构造新字符串对象时,会生成新的 char[] value,对内容进行复制 。这种通过创建副本对象来避免共享的手段称之为【保护性拷贝(defensive copy)】

享元模式

前面介绍的不可变对象的设计,为了保证对象的不可变性,利用了一种保护性拷贝的机制,有修改情况的时候,创建一个新的对象。这样虽然避免共享,保证这些方法的线程安全,但是也带来一些问题——对象创建的太频繁——所以关联一个设计模式——享元模式(GOF23种设计模式之一)。

简介

定义 英文名称:Flyweight pattern. 当需要重用数量有限的同一类对象时
wikipedia: A flyweight is an object that minimizes memory usage by sharing as much data aspossible with other similar objects
出自 “Gang of Four” design patterns
归类 Structual patterns

体现

包装类

在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 cachereturn 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 串池

BigDecimal BigInteger

包装类这些,他们都是不可变类,体现的都是享元模式,是线程安全的。
辨析
前面提到的不可变类,他们的线程安全性,都是指单个方法运行时是线程安全的,但他们多个方法的组合不能保证线程的安全。所以需要使用额外的手段保障他们的线程安全性。

DIY

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

@Slf4j(topic = "c.Pool")
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 {private String name;public MockConnection(String name) {this.name = name;}@Overridepublic String toString() {return "MockConnection{" +"name='" + name + '\'' +'}';}/*下面是导入Connection的重写类,太多了,省略*/
}

使用连接池:

public class Test3 {public static void main(String[] args) {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等 对于更通用的对象池,可以考虑使用apache
commons pool,例如redis连接池可以参考jedis中关于连接池的实现

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计不外流,保护创意的同时锁住图纸安全!

在设计行业中&#xff0c;图纸和创意文稿的安全至关重要&#xff0c;因为它们体现了企业的创新能力和核心竞争力。华企盾DSC数据防泄密系统提供了一系列功能&#xff0c;可以有效地保护这些珍贵的设计和文档不被外泄。以下是如何利用华企盾DSC系统保障设计图纸安全的关键措施&a…

stable-diffusion教程

下载代码 https://gitee.com/stable_diffusion/stable-diffusion-webui.git 然后执行自动安装环境命令 ./webui.sh

MySQL怎么看死锁记录

这个结果分成三部分&#xff1a; (1) TRANSACTION&#xff0c;是第一个事务的信息&#xff1b; (2) TRANSACTION&#xff0c;是第二个事务的信息&#xff1b; (3)WE ROLL BACK TRANSACTION (1)&#xff0c;是最终的处理结果&#xff0c;表示回滚了第一个事务。 第一个事务的信…

基于飞腾D2000全国产化高速公路一体化收费站解决方案:站数据服务器、站AI服务器、收费系统、监控系统

高速公路一体化收费站解决方案 行业 交通工程及沿路设施作为公路的一个重要组成部分&#xff0c;对城市互联和城市发展具有重要意义&#xff0c;因此围绕高速公路的专用收费 站设计和建设&#xff0c;将有效促进枢纽集散系统与高速公路连通&#xff0c;显著提升城市高速集散能…

ansible-playbook离线升级centos内核

目录 概述实践ansible目录结构关键代码执行效果 结束 概述 内核离线包官网下载地址如下&#xff1a; 地址 实践 ansible目录结构 如对 ansible 不熟悉&#xff0c;离线包下载有问题&#xff0c;请至此地址下载&#xff0c;按本文操作可直接使用。 相关文章链接如下 文章地…

通过共享网络使树莓派4联网

一、问题 尝试配置/boot/dhcpcd.conf文件无效&#xff0c;wifi依然无法联网&#xff0c;且通过桌面选择wifi输入密码后同样无法联网&#xff1b; 二、环境 1、可以通过网线连接电脑&#xff0c;并且可以连接串口&#xff1b; 2、可以通过静态地址通过网线访问树莓派ssh端口&…

交叉调制少样本图像生成用于结直肠组织分类

文章目录 Cross-Modulated Few-Shot Image Generation for Colorectal Tissue Classification摘要方法实验结果 Cross-Modulated Few-Shot Image Generation for Colorectal Tissue Classification 摘要 提出问题&#xff1a; 针对罕见癌症组织的组织病理训练数据稀缺问题&…

【ARMv9 DSU-120 系列 9 -- DSU-120 Debug block】

请阅读【Arm DynamIQ™ Shared Unit-120 专栏 】 请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 文章目录 ARM DSU-120 Debug BlockCluster debug componentsCache DebugDSU-120 Cache Debug 特性应用场景Terminology处理元素(PE)

maven-idea新建和导入项目

全局配置 新建项目 需要新建的文件夹 src/testsrc/test/javasrc/main/java 注&#xff1a;1、新建Java-class&#xff0c;输入.com.hello.hellomaven 2、快捷键psvm显示 public static void main(String[] args) {.... } package com.hello;public class hellomaven {publ…

K8S Service 常见问题

Service 问题排查 为了演示需要部署以下服务。 apiVersion: apps/v1 kind: Deployment metadata:name: busyboxnamespace: appslabels:app: busybox spec:replicas: 1selector:matchLabels:app: busyboxtemplate:metadata:labels:app: busyboxspec:containers:- name: busybo…

案例分享:使用RabbitMQ消息队列和Redis缓存优化Spring Boot秒杀功能

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

golang垃圾回收

4.29 Golang中GC回收机制三色标记与混合写屏障 第14讲-总结哔哩哔哩bilibili 三个阶段 gc垃圾回收 早期是 GO1.3 标记清除  之前的标记清除&#xff1a;开始标记找到可达对象&#xff0c;并标记&#xff0c;标记完后清楚未标记的 较长时间的STW&#xff0c;使程序暂停…

抄表自动化的实现与优势

1.界定与简述 抄表自动化是一种当代关键技术&#xff0c;致力于取代传统的手动式抄表方法&#xff0c;通过远程数据数据采集解决&#xff0c;完成电力工程、水、气等公用事业电力仪表的全自动载入。这一系统利用先进的感应器、物联网技术(IoT)设备及数据数据分析工具&#xff…

Centos7安装K8S集群环境

一、系统设置 1、关闭swap 临时关闭swap swapoff -a 永久关闭 注释掉 /etc/fstab 中的下面配置 #/dev/mapper/centos-swap swap swap defaults 0 0 2、 关闭SELinux kubelet不支持SELinux, 这里需要将SELinux设置为permissive模式 setenforce 0 sed -i s/^SELINUXenfo…

如何制作一个后台管理页面的路由以及功能实现

后台 文章目录 后台一、RESFUL API二、各模块路由处理1、分类模块1.1、GET /list 分类列表1.2、POST / 新增|编辑分类1.3、DELETE / 删除分类1.4、GET /option 分类选项列表 2、评论模块2.1、GET /list 评论列表2.2、DELETE / 删除评论2.3、PUT /review 修改评论审核 3、留言模…

企业私服中使用Maven,标准的setting.xml文件

Maven Maven是一个项目管理和理解工具。它主要服务于以下几个方面: 构建管理:Maven可用于构建和管理任何基于Java平台的项目。 依赖管理:Maven有一个中央仓库,用于保存大量常用的库文件。当进行项目构建时,Maven会自动下载所需的库文件到本地仓库,这极大地简化了库文件…

Linux基础IO(下)

目录 1. 缓冲区 1.1 定义 1.2 理解缓冲区 1.2.1 为什么要有缓冲区 1.2.2 缓冲区的工作原理 缓冲区什么时候写入&#xff0c;什么时候刷新&#xff1f; 2. 文件系统 2.1 什么是文件系统&#xff1f; 2.2 为什么要有文件系统&#xff1f; 2.3 认识文件的管理结构 2.…

机器学习:深入解析SVM的核心概念(问题与解答篇)【二、对偶问题】

对偶问题 **问题一&#xff1a;什么叫做凸二次优化问题&#xff1f;而且为什么符合凸二次优化问题&#xff1f;**为什么约束条件也是凸的半空间&#xff08;Half-Space&#xff09;凸集&#xff08;Convex Set&#xff09;半空间是凸集的例子SVM 约束定义的半空间总结 **问题二…

Flutter创建自定义的软键盘

参考代码&#xff1a; Flutter - Create Custom Keyboard Examples 本文贴出的代码实现了一个输入十六进制数据的键盘&#xff1a; &#xff08;1&#xff09;支持长按退格键连续删除字符&#xff1b; &#xff08;2&#xff09;可通过退格键删除选中的文字&#xff1b; &…

React 模板选择标准

在决定开发过程中采用哪个React模板时&#xff0c;这确实是一个需要考虑多方面因素的挑战。因为这不仅关乎应用程序的初始构建&#xff0c;更涉及其后续的扩展性、可维护性和整体性能。那么&#xff0c;面对众多现成的模板&#xff0c;我们如何挑选出最适合的解决方案呢&#x…