分析
这是一种无锁
的设计模式,因为不可变对象没有任何机会去修改这个对象的属性或者引用类型
需要有两个概念
- 不可变对象一定是线程安全的
- 可变对象不一定是不安全的
创建不可见对象的原则:
1)所有成员变量必须是private
2)最好同时用final修饰(非必须)
3)不提供能够修改原有对象状态的方法
-
最常见的方式是不提供setter方法
-
如果提供修改方法,需要新创建一个对象,并在新创建的对象上进行修改
4)通过构造器初始化所有成员变量,引用类型的成员变量必须进行深拷贝(deep copy)
5)getter方法不能对外泄露this引用以及成员变量的引用
6)最好不允许类被继承(非必须)
首先,String类、包装类就是个不可变的对象
我们经常会用他们来作为HashMap的key,试想一下如果这些类是可变的,将会发生什么?后果不可预知,这将会大大增加Java代码编写的难度。
举例
- 对于
基本包装类和String类型
,本身都是不可变的对象,所以不存在线程不安全问题 - 对于
集合类,可以利用Collections.unmodifiableList()方法
,防止被修改
public class Test {
private final int age;
private final String name;
private final List<String> list ;
public Test(int age, String name) {
this.age = age;
this.name = name;
list = new ArrayList<>();
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public List<String> getList() {
return Collections.unmodifiableList(list);
}
}
对比不可变对象
public class Performance {
public static void main(String[] args) {
long startTimestamp = System.currentTimeMillis();
SynObj synObj = new SynObj();
synObj.setName("Alex");
for (long l = 0L ;l<100000000;l++){
synObj.toString();
}
long endTimestamp = System.currentTimeMillis();
System.out.println("可变对象耗时:" + (endTimestamp-startTimestamp));
startTimestamp = System.currentTimeMillis();
ImmutableObj immutableObj = new ImmutableObj("Alex");
for (long l = 0L ;l<100000000;l++){
immutableObj.toString();
}
endTimestamp = System.currentTimeMillis();
System.out.println("不可变对象耗时:" + (endTimestamp-startTimestamp));
}
}
/** * 不可变对象 */
final class ImmutableObj{
private final String name;
ImmutableObj(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "ImmutableObj{" +
"name='" + name + '\'' +
'}';
}
}
/** * 可变对象,需要加锁 */
class SynObj{
private String name;
public synchronized void setName(String name){
this.name = name;
}
@Override
public synchronized String toString() {
return "SynObj{" +
"name='" + name + '\'' +
'}';
}
}
结果:
可变对象耗时:4267
不可变对象耗时:1698
- 可以发现不可变对象的性能提高的一倍以上