java不可变对象线程安全_不可变真的意味线程安全?

并发主题

不可变真的意味线程安全?

我们常看到:"如果对象是不可改变的,它是线程安全的。"但是是真的吗?

何为不可变?一个不可变对象是指一个对象一旦构建后的状态就不会改变。

看下面String类的一段代码:

public class String {

private final char value[];

/** Cache the hash code for the string */

private int hash; // Default to 0

public String(char[] value) {

this.value = Arrays.copyOf(value, value.length);

}

public int hashCode() {

int h = hash;

if (h == 0 && value.length > 0) {

char val[] = value;

for (int i = 0; i < value.length; i++) {

h = 31 * h + val[i];

}

hash = h;

}

return h;

}

}

String是不可变的,但是我们发现hashCode发现在改变了内部状态值int hash;看其实现可以总结,不可变可以改变其内部状态,只要这种改变不要对外暴露。

我们重写一下hasCode代码如下:

public int hashCode() {

if (hash == 0 && value.length > 0) {

char val[] = value;

for (int i = 0; i < value.length; i++) {

hash = 31 * hash + val[i];

}

}

return hash;

}

这时就是线程不安全了,我们移走了本地变量h,直接基于内部状态进行改变操作。如果同时有几个线程调用这个方法,返回的值可能是不同的。

那么这个类是不可改变的吗?由于两个不同的线程可以看到不同的哈希码,在外部的角度来看,我们的状态变化,所以它并不是一成不变的。

所以我们可以得出结论,String是不可改变的,因为它是线程安全的,而不是相反。所以...如果说:"做一些不可变的对象,这样它就是线程安全的!但一定要注意,首先这个不可变对象必须是线程安全的!"

我们看看时间格式使用:

public class VerySimpleDateFormat {

private final DateFormat formatter = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT);

public String format(Date d){

return formatter.format(d);

}

}

这个不是线程安全的,因为formatter.format方法不是。

这是不可变的对象?好问题!我们已经尽最大努力使所有字段不能修改,我们不使用任何的setter或任何方法来讲对象的状态改变。其实,内部SimpleDateFormat自己改变了其状态,这使得它不是线程安全的。

这个例子得出结论,不容易实现一个不可变类。 final关键字是不够的,你必须确保你的对象字段不被改变其状态,有时这又是不可能的。(如果按照DDD设计成值对象就从语义上保证不可变了)

看看这段代码:

public class HelloAppender {

private final String greeting;

public HelloAppender(String name) {

this.greeting = 'hello ' + name + '!\n';

}

public void appendTo(Appendable app) throws IOException {

app.append(greeting);

}

}

虽然有final方法,但是Appendable改变了状态,这个类不是不可变的,是可变的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值