Immutable不變模式
併發問題是由於不同線程共同操作共享內存,導致讀寫順序錯亂,影響了共享內存的值。
那麼,如果線程只讀操作共享內存,就不存在併發問題了。
1. 如何實現不變性
第一,需要類本身是final的,防止子類繼承他而修改不變性。
第二,需要類的屬性都是final的,並且只存在讀方法。
第三,使用深拷貝修改對象,簡單說就是在修改對象時,new一個新的對象返回。但是一方面考慮麻煩,另一方面clone方法是native方法,效率更高,一般我們還是選擇使用clone方法實現。
Java中有一個很好的例子,就是String類。請移步-> https://blog.csdn.net/sougou_1323/article/details/103223855
2. 享元模式
簡單說,享元模式就是創建緩存,Java中的包裝類除了Float和Double都有緩存。請移步 2. 3)-> https://blog.csdn.net/sougou_1323/article/details/103227907
這裏需要注意一點,請不要使用包裝類型作爲鎖,如下面的栗子,al和bl看上去是兩個鎖,實際上是一個鎖,這樣很容易出問題。
class A {
Integer al = Integer.valueOf(1);
public void setA() {
synchronized (al) {
// ...
}
}
}
class B {
Integer bl = Integer.valueOf(1);
public void setB() {
synchronized (bl) {
// ...
}
}
}
3. 假不變性
假?請看下面的栗子。這裏的問題本質是,Java中都是引用,user引用是final的,但是User對象不是final的。換句話說,user的引用確實不可變,但是user實例的內容是可變的。所以在使用不變性模式的時候一定要確認是否要求屬性對象也具備不可變性。
public final class Shop{
private final User user;
public Account(User user){
this.user = user;
}
public User getUser(){
return this.user;
}
}
public class User {
private String name;
private Integer age;
public void addAge() {
age += 1;
}
}
還有一種方法,將getUser方法配合不變性,不將Shop的屬性user返回,而是將User複製一份返回。這樣不管外界拿到user怎麼折騰都不會影響到Shop的屬性user,也就保證了不變性,但是這種實現方式比較消耗內存,可能會引起頻繁的GC,具體還要看實際情況來決定。