Immutable
Immutable(不可變的),Immutable角色是一個類,在這個角色中,字段的值不可修改,也不存在修改字段內容的方法。Immutable角色的實例被創建後,狀態將不再發生變化。無需將Immutable角色的方法聲明爲synchronized
Immutable模式的類圖:
何時使用Immutable:
1. 實例創建後,狀態不再發生變化時
字段聲明爲final,不存在setter方法
2. 實例是共享的,且被頻繁訪問時
不需要使用synchronized,這樣就能提高性能。
java標準庫類中有成對的mutable和immutable類
java.lang.StringBuffer (mutable類)和java.lang.String(immutable類)
所以如果需要頻繁修改字符串內容,則使用StringBuffer(改寫時需要妥善使用synchronized);
如果不需要修改字符串內容,只是引用其內容。
標準類庫中用到的Immutable模式:
表示字符串的java.lang.String類
表示大數字的java.math.BigInteger類,java.math.BigInteger
表示正則表達式的java.util.regex.Pattern類等
非線程安全的java.util.ArrayList的類
一下是實例代碼
public class WriterThread extends Thread{
private final List<Integer> list;
public WriterThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
for (int i = 0;true; i++) {
list.add(i);
list.remove(0);
}
}
}
public class ReaderThread extends Thread{
private final List<Integer> list;
public ReaderThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
while (true) {
for (int n:list) {
System.out.println(n);
}
}
}
}
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
new WriterThread(list).start();
new ReaderThread(list).start();
}
}
當ArrayList在被多個線程同時進行讀寫操作時而失去安全性時,便會拋出
Exception in thread "Thread-1" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at day2.ReaderThread.run(ReaderThread.java:15)
利用Collections.synchronizedList方法進行的同步
java.util.ArrayList是非線程安全的類,但如果使用Collections.sysnchronizedList方法進行同步,就能得到線程安全的實例。
public class ReaderThread extends Thread{
private final List<Integer> list;
public ReaderThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
while(true){
synchronized (list) {
for(int n:list){
System.out.println(n);
}
}
}
}
}
public class Main {
public static void main(String[] args) {
//final只是說你的當前這個list不能指向內存其他地方了
final List<Integer> list= Collections.synchronizedList(new ArrayList<Integer>());
new WriterThread(list).start();
new ReaderThread(list).start();
}
}
WriterThread與前面例子代碼用例一致,因爲寫線程不斷改寫值,所以讀取的值是跳躍的
CopyOnWriteArrayList類
採用copy-on-write技術避免讀寫衝突,所謂copy-on-write,就是寫時複製的意思,當集合執行寫操作的時候,內部已確保安全的數組就會被整體複製。
實例代碼:讀寫線程類與ArrayList的實例相同
public class Main {
public static void main(String[] args) {
final List<Integer> list = new CopyOnWriteArrayList<Integer>();
new ReaderThread(list).start();
new WriterThread(list).start();
}
}
Immutable模式:
當一個類的實例創建完成後,其狀態就完全不會發生變化。這時,該類的方法就算被多個線程同時執行也沒關係,所以這些方法就無需聲明爲synchronized,這樣就可以在生存型和安全性不缺失的同時提高性能。
下面的s被replace替換成了CAT,有人覺得這樣違背了immutable的模式,實際上着個字符串s沒有變,只是replace這個方法新建了一個實例而已。
public static void main(String[] args) {
String s = “BAT”;
System.out.print(s.replace(‘B’,’C’));
}
如下代碼,getInfo方法獲取的info字段中保存的實例並不是String實例,而是StringBuffer實例,StringBuffer類與String類不同,包含修改內部狀態的方法,所以info子彈的內容也可以被外部修改,String類的replace方法並不會修改實例本身,但StringBuffer的replace會修改實例本身,由於info字段聲明瞭final,所以info字段的值本身並不會改變,但是info字段所指向的實例的狀態卻可能發生改變。
public final class UserInfo {
private final StringBuffer info;
public UserInfo(String name,String address){
this.info = new StringBuffer("<info name=\""+name+"\"address=\""+address+"\"+/>");
}
public StringBuffer getInfo(){
return info;
}
public String toString(){
return "[userInfo:"+info+"]";
}
}
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("Alice", "America");
System.out.println("userinfo="+userinfo);
//修改狀態
StringBuffer info = userinfo.getInfo();
System.out.println(info);
info.replace(12, 17, "Boddy");
System.out.println("userinfo="+userinfo);
}