Java并发不可变篇

不可变

日期转换的问题

问题提出

下面代码运行时,由于SimpleDateFormat不是线程安全的

java 复制代码
@Slf4j(topic = "c.TestDemo")
public class TestDemo {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    log.debug("{}",sdf.parse("1951-04-21"));
                } catch (Exception e) {
                    log.debug("{}",e);
                }
            }).start();
        }
    }
}

使用DateTimeFormatter(被final修饰是线程安全且不可变的)

不可变设计

以String为例,说明一下不可变设计的要素

java 复制代码
public final class String implements 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的使用

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

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

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

保护性拷贝

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

java 复制代码
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 做出了修改:

java 复制代码
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;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

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

享元模式(具体信息可以看之前发的享元模式介绍那篇文章)

无状态

设计Servlet时为了保证其线程安全,都会有这样的建议,不要为Servlet设置成员变量,这种没有任何成员变量的类是线程安全的(因为成员变量保存的数据也可以称为状态信息,因此没有成员变量就称为无状态)

相关推荐
鬼火儿21 分钟前
15.<Spring Boot 日志>
java·后端
Mos_x22 分钟前
SpringBoot】Spring Boot 项目的打包配置
java·后端
qianbailiulimeng25 分钟前
【Spring Boot】Spring Boot解决循环依赖
java·后端
何中应26 分钟前
Spring Boot解决循环依赖的几种办法
java·spring boot·后端
donotshow26 分钟前
SpringBoot】Spring Boot 项目的打包配置
java·后端
zhangfeng113326 分钟前
亲测有效的mem 流行病预测,时间序列预测,r语言做移动流行区间法,MEM流行病阈值设置指南
开发语言·r语言·生物信息
鬼火儿27 分钟前
Spring Boot 整合 ShedLock 处理定时任务重复
java·后端
王元_SmallA29 分钟前
【Spring Boot】Spring Boot解决循环依赖
java·后端
小圆53138 分钟前
java-learn(9):常见算法,collection框架
java·开发语言·算法
nbsaas-boot1 小时前
SaaS 租户上下文传播架构
java·架构·saas