热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JUC笔记共享模型之不可变

JUC-共享模型之不可变一、不可变类设计二、final原理三、享元设计模式四、实现一个简单的连接池五、State一、不可变类设计不可变:如果一个对象不能够修改其内部


JUC-共享模型之不可变

    • 一、不可变类设计
    • 二、final
      • 原理
    • 三、享元设计模式
    • 四、实现一个简单的连接池
    • 五、State




一、不可变类设计

不可变:如果一个对象不能够修改其内部状态(属性),那么就是不可变对象

不可变对象线程安全的,不存在并发修改和可见性问题,是另一种避免竞争的方式

String 类也是不可变的,该类和类中所有属性都是final


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

  • 无写入方法(set)确保外部不能对内部属性进行修改

  • 属性用final修饰保证了该属性是只读的,不能修改

    public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];//....
    }

  • 更改 String 类数据时&#xff0c;会构造新字符串对象&#xff0c;生成新的 char[] value&#xff0c;通过创建副本对象来避免共享的方式称之为保护性拷贝


二、final


原理

public class TestFinal {final int a &#61; 20;
}

字节码&#xff1a;

0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: bipush 20 // 将值直接放入栈中
7: putfield #2 // Field a:I
<-- 写屏障
10: return

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

其他线程访问 final 修饰的变量会复制一份放入栈中&#xff0c;效率更高




三、享元设计模式


  • 简介定义英文名称&#xff1a;Flyweight pattern, 重用数量有限的同一类对象。
    • 结构型模式

享元模式的体现:

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

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

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

2、String 串池
3、BigDecimal, BigInteger

在这里插入图片描述


四、实现一个简单的连接池

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

/*** Description: 简易连接池** &#64;author guizy* &#64;date 2020/12/29 21:21*/
public class Test2 {public static void main(String[] args) {/*使用连接池*/Pool pool &#61; new Pool(2);for (int i &#61; 0; i < 5; i&#43;&#43;) {new Thread(() -> {Connection conn &#61; pool.borrow();try {Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}pool.free(conn);}).start();}}
}&#64;Slf4j(topic &#61; "guizy.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 &#61; poolSize;this.connections &#61; new Connection[poolSize];this.states &#61; new AtomicIntegerArray(new int[poolSize]);//使用AtomicIntegerArray保证states的线程安全for (int i &#61; 0; i < poolSize; i&#43;&#43;) {connections[i] &#61; new MockConnection("连接" &#43; (i &#43; 1));}}// 5. 借连接public Connection borrow() {while (true) {for (int i &#61; 0; i < poolSize; i&#43;&#43;) {// 获取空闲连接if (states.get(i) &#61;&#61; 0) {if (states.compareAndSet(i, 0, 1)) {//使用compareAndSet保证线程安全log.debug("borrow {}", connections[i]);return connections[i];}}}// 如果没有空闲连接&#xff0c;当前线程进入等待, 如果不写这个synchronized,其他线程不会进行等待, // 一直在上面while(true), 空转, 消耗cpu资源synchronized (this) {try {log.debug("wait...");this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}// 6. 归还连接public void free(Connection conn) {for (int i &#61; 0; i < poolSize; i&#43;&#43;) {if (connections[i] &#61;&#61; 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 &#61; name;}&#64;Overridepublic String toString() {return "MockConnection{" &#43;"name&#61;&#39;" &#43; name &#43; &#39;\&#39;&#39; &#43;&#39;}&#39;;}// Connection 实现方法略
}

22:01:07.000 guizy.Pool [Thread-2] - wait...
22:01:07.000 guizy.Pool [Thread-0] - borrow MockConnection{name&#61;&#39;连接1&#39;}
22:01:07.005 guizy.Pool [Thread-4] - wait...
22:01:07.000 guizy.Pool [Thread-1] - borrow MockConnection{name&#61;&#39;连接2&#39;}
22:01:07.006 guizy.Pool [Thread-3] - wait...
22:01:07.099 guizy.Pool [Thread-0] - free MockConnection{name&#61;&#39;连接1&#39;}
22:01:07.099 guizy.Pool [Thread-2] - wait...
22:01:07.099 guizy.Pool [Thread-3] - borrow MockConnection{name&#61;&#39;连接1&#39;}
22:01:07.099 guizy.Pool [Thread-4] - wait...
22:01:07.581 guizy.Pool [Thread-3] - free MockConnection{name&#61;&#39;连接1&#39;}
22:01:07.582 guizy.Pool [Thread-2] - borrow MockConnection{name&#61;&#39;连接1&#39;}
22:01:07.582 guizy.Pool [Thread-4] - wait...
22:01:07.617 guizy.Pool [Thread-1] - free MockConnection{name&#61;&#39;连接2&#39;}
22:01:07.618 guizy.Pool [Thread-4] - borrow MockConnection{name&#61;&#39;连接2&#39;}
22:01:07.955 guizy.Pool [Thread-4] - free MockConnection{name&#61;&#39;连接2&#39;}
22:01:08.552 guizy.Pool [Thread-2] - free MockConnection{name&#61;&#39;连接1&#39;}

在这里插入图片描述


五、State

无状态&#xff1a;成员变量保存的数据也可以称为状态信息&#xff0c;无状态就是没有成员变量

Servlet 为了保证其线程安全&#xff0c;一般不为 Servlet 设置成员变量&#xff0c;这种没有任何成员变量的类是线程安全的



参考&#xff1a;


  • Java并发编程(八) : 不可变对象设计(String保护性拷贝)、final的使用、享元模式
  • JUC-无锁

推荐阅读
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 配置IPv4静态路由实现企业网内不同网段用户互访
    本文介绍了通过配置IPv4静态路由实现企业网内不同网段用户互访的方法。首先需要配置接口的链路层协议参数和IP地址,使相邻节点网络层可达。然后按照静态路由组网图的操作步骤,配置静态路由。这样任意两台主机之间都能够互通。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 本文介绍了Redis中RDB文件和AOF文件的保存和还原机制。RDB文件用于保存和还原Redis服务器所有数据库中的键值对数据,SAVE命令和BGSAVE命令分别用于阻塞服务器和由子进程执行保存操作。同时执行SAVE命令和BGSAVE命令,以及同时执行两个BGSAVE命令都会产生竞争条件。服务器会保存所有用save选项设置的保存条件,当满足任意一个保存条件时,服务器会自动执行BGSAVE命令。此外,还介绍了RDB文件和AOF文件在操作方面的冲突以及同时执行大量磁盘写入操作的不良影响。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 如何使用代理服务器进行网页抓取?
    本文介绍了如何使用代理服务器进行网页抓取,并探讨了数据驱动对竞争优势的重要性。通过网页抓取,企业可以快速获取并分析大量与需求相关的数据,从而制定营销战略。同时,网页抓取还可以帮助电子商务公司在竞争对手的网站上下载数百页的有用数据,提高销售增长和毛利率。 ... [详细]
  • 网卡工作原理及网络知识分享
    本文介绍了网卡的工作原理,包括CSMA/CD、ARP欺骗等网络知识。网卡是负责整台计算机的网络通信,没有它,计算机将成为信息孤岛。文章通过一个对话的形式,生动形象地讲述了网卡的工作原理,并介绍了集线器Hub时代的网络构成。对于想学习网络知识的读者来说,本文是一篇不错的参考资料。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
author-avatar
此女人不嫁_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有