不可变类和final

public static void main(String[] args) {
    // DateTimeFormatter是不可变对象,线程安全
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            log.debug("{}", formatter.parse("1951-04-21"));
        }).start();
    }
    //test();
}
// SimpleDateFormat是线程不安全的
private static void test() {
    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.error("{}", e);
            }
        }).start();
    }
}
// 包装类,字符串,BigDecimal,BigInteger是不可变类,都体现了享元模式,是线程安全的
class AccountCas implements Account{
    private AtomicReference<BigDecimal> balance;
    public AccountCas(BigDecimal balance){
        this.balance = new AtomicReference<>(balance);
    }

    public BigDecimal getBalance(){
        return balance.get();
    }
    public void withdraw(BigDecimal amount){
        while(true){
        // 每个单个方法都是线程安全的,但是多个方法的组合不是线程安全的
        // 上一个线程还没有来得及减,当前线程拿到的又是之前的值,此时就会有线程安全
        // 所以要使用AtomicReference进行保护
            BigDecimal prev = balance.get();
            BigDecimal next = prev.subtract(amount);
            if(balance.compareAndSet(prev,next)){
                break;
            }
        }
    }
}

如何保证不可变性
类是final的:不可继承,所有方法不会被覆盖,防止子类无意间破环不可变性
属性是final的:只读,不可修改

// String类中
// 保护性拷贝
// 重新生成一个副本(深拷贝),而不是直接指向(浅拷贝),避免共享
this.value = Arrays.copyOf(value, value.length);

// String虽然避免了共享,但是对象创建太频繁

享元模式

最小化内存使用

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);
}
// Boolean,Byte,short,Integer等包装类提供了valueOf方法
// Byte,Short,Long缓存范围都是-128-127
// Character缓存范围是0-127
// Integer也是-128-127,最小值不能变,但最大值可以通过调整虚拟机参数
// -Djava.lang.Integer.IntegerCache.high
// Boolean缓存了TRUE和FALSE

final原理

在赋值之后加入写屏障
写屏障之前的指令不会被重排序
写屏障之前的赋值操作会被同步到主存

static final int A = 10; 
static final int B = Short.MAX_VALUE + 1;
读取final变量时,是直接将其值10(复制到方法的栈中,较大会复制到常量池中)拿到(BIPUSH 10, LDC 32768)
如果不加final,会用getstatic指令获取变量的值(GETSTATIC cn/itcast/n5/TestFinal.A)堆中

Servlet没有成员变量,这种没有成员变量的类也是线程安全的
final
享元模式在String中的应用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值