• 共享模型之不可变


    1 日期转换的问题

    1. public static void main(String[] args) {
    2. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(() -> {
    5. try {
    6. log.debug("{}", sdf.parse("1951-04-21"));
    7. } catch (Exception e) {
    8. log.error("{}", e);
    9. }
    10. }).start();
    11. }
    12. }

    有很大几率出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果

     1.1 同步锁

    1. public static void main(String[] args) {
    2. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(() -> {
    5. synchronized (sdf) {
    6. try {
    7. log.debug("{}", sdf.parse("1951-04-21"));
    8. } catch (Exception e) {
    9. log.error("{}", e);
    10. }
    11. }
    12. }).start();
    13. }
    14. }

    这样虽能解决问题,但带来的是性能上的损失,并不算很好

    1.2 不可变

    DateTimeFormatter是Java 8引入的新的日期时间格式化类,具有更好的线程安全性和更强大的解析能力,而SimpleDateFormat是早期版本中的日期时间格式化类,使用起来相对简单。

    1. public static void main(String[] args) {
    2. DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(() -> {
    5. try {
    6. log.debug("{}", sdf.parse("1951-04-21"));
    7. } catch (Exception e) {
    8. log.error("{}", e);
    9. }
    10. }).start();
    11. }
    12. }

    2.不可变设计

    以String来举例

    1. public final class String
    2. implements java.io.Serializable, Comparable, CharSequence {
    3. /** The value is used for character storage. */
    4. private final char value[];
    5. /** Cache the hash code for the string */
    6. private int hash; // Default to 0
    7. // ...
    8. }

    2.1 final 的使用

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

     2.2 保护性拷贝

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

    2.3 final原理

    设置的原理
    final变量之前会有一个写屏障,也就是前面的赋值操作不可能走到后面保证了a的值是最新的值。如果不加上a,那么就有可能在多线程调用的时候,a初始值是0,但是还需要赋值一个20,但是由于线程切换,而且a已经算是赋值成功,那么就会直接被使用。如果加上final,那么就会直接给变量赋值20而不是先赋值0再赋值20。

    优化原理
    如果是final被线程方法调用,那么就会直接把final的值复制一份给这个线程的栈。如果是值太大,那么就会通过字节码指令ldc来存入串池中。如果没有final那么是直接从堆内存中拿,很容易出现线程的问题
     

    3.享元模式

    享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。在享元模式中,对象被细粒度地分解为可共享的和不可共享的部分。

    通常情况下,一个应用程序中可能会有大量相似的对象,这些对象的大部分属性都是相同的,只有少数属性的值不同。创建和使用这些对象会消耗很多内存,为了优化性能和减少内存占用,可以使用享元模式。

    享元模式的核心思想是共享对象。它将对象的状态(即不可变的属性)和可变的状态分离,并通过共享不可变的状态来减少内存使用。可变状态则由外部对象管理。

    在享元模式中,共享对象被存储在一个池中(即享元池),需要创建对象时,首先在享元池中查找是否已存在相同状态的对象,如果存在,则直接返回共享的对象;如果不存在,则创建新的对象并将其添加到享元池中。

    享元模式的优点包括:

    1. 减少内存使用:通过共享相同状态的对象,可以减少内存使用,特别是当有大量相似对象时。
    2. 提高性能:共享对象的重复利用可以提高程序的执行效率。
    3. 隔离外部状态:将对象的可变状态分离到外部,使得对象在共享的同时,对外部状态的变化是相互独立的。

    然而,享元模式也有限制和注意事项:

    1. 对象的内部状态和外部状态需要分离,外部状态不能影响对象的行为。
    2. 共享对象需要是不可变的,否则会导致对象状态的不一致。
    3. 对象的共享和创建逻辑需要细心设计,避免并发问题和错误的共享对象。

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

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

    3.1 连接池

    1. @Slf4j
    2. public class MyPool {
    3. public static void main(String[] args) {
    4. Pool pool = new Pool(2);
    5. for(int i=0;i<5;i++){
    6. new Thread(()->{
    7. Connection conn = pool.borrow();
    8. try {
    9. Thread.sleep(new Random().nextInt(1000));
    10. } catch (InterruptedException e) {
    11. e.printStackTrace();
    12. }
    13. pool.free(conn);
    14. },"t"+i).start();
    15. }
    16. }
    17. }
    18. @Slf4j(topic = "c.Pool")
    19. class Pool{
    20. private final int poolsize;
    21. private Connection[] connections;
    22. private AtomicIntegerArray states;
    23. public Pool(int size){
    24. this.poolsize=size;
    25. connections=new MyConnection[size];
    26. int[] ints = new int[size];
    27. Arrays.fill(ints,0);
    28. states=new AtomicIntegerArray(ints);
    29. for(int i=0;i
    30. connections[i]=new MyConnection("连接" &
  • 相关阅读:
    bootstrap-select js jQuery控制select属性变化
    Unable to find main class
    Mac PF命令防火墙
    ES6之解构参数
    微前端原理解析
    Java 模拟实现 定时器 和 线程池
    2023.9.23 关于 HTTP 详解
    mybatis-plus常用注解@TableId、@TableField
    Studio 3T工具下载安装及使用教程
    函数式编程-Stream流
  • 原文地址:https://blog.csdn.net/weixin_50458070/article/details/133880166