單例模式的基本介紹
雖然單例模式中的 HungerSingleton 可以保證只產生一個實體,但其中的資料還是需要做好同步化的動作。
首先標準的 Hunger Singleton
public class HungerSingleton { private static final HungerSingleton sUniqueInstance = new HungerSingleton(); private List<Integer> mNumbers = new ArrayList<Integer>(); private HungerSingleton() { // Prevents singleton instance being instantiated from outside } public static HungerSingleton getInstance() { return sUniqueInstance; } public void addNumber(int number) { mNumbers.add(number); } public List<Integer> getNumbers() { return mNumbers; } }
唯一不同的為第5行 mNumbers , 這個 List 將拿來做測試用,確保資料的同步化是否真的實現。
接著為Thread subclass
public class TestThread extends Thread { @Override public void run() { for (int i = 0; i < 10; ++i) { HungerSingleton.getInstance().addNumber(i); } } }
相當簡單,在 run 中會取得 HungerSingleton 的實體並把數值加到 mNumbers 中。
測試 Main class
public class Main { public static void main(String[] args) { Thread thread = new TestThread(); thread.start(); Thread thread2 = new TestThread(); thread2.start(); try { Thread.currentThread().sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } for (Integer number : HungerSingleton.getInstance().getNumbers()) { System.out.println("number:" + number); } } }
目標為讓 HungerSingleton 的 mNumbers 內容為 2 組 0 到 10 按照順序排列的數值。
執行結果
number:0 number:0 number:1 number:1 number:2 number:3 number:2 number:4 number:3 number:5 number:5 number:4 number:7 number:6 number:8 number:9 number:6 number:7 number:8 number:9
可以看到因為多執行緒的關係造成 mNumbers 內容排序錯亂。
加上同步化機制
public class TestThread extends Thread { @Override public void run() { synchronized (HungerSingleton.getInstance()) { for (int i = 0; i < 100; ++i) { HungerSingleton.getInstance().addNumber(i); } } } }
synchronized 可以讓一個執行緒取得指定物件的鎖,並擋住其他執行緒,直到該進入的執行緒完成函式內動作退出。
執行結果
number:0 number:1 number:2 number:3 number:4 number:5 number:6 number:7 number:8 number:9 number:0 number:1 number:2 number:3 number:4 number:5 number:6 number:7 number:8 number:9
從實驗結果可以看到雖然 HungerSingleton 可以保證只產生一個實體,但其內資料的操作還是需要加上同步化的動作。