<a href="http://my.oschina.net/noahxiao/blog/126357#OSC_h3_1">一、問題背景</a>
<a href="http://my.oschina.net/noahxiao/blog/126357#OSC_h3_2">二、問題分析</a>
<a href="http://my.oschina.net/noahxiao/blog/126357#OSC_h3_3">三、問題修正</a>
<a href="http://my.oschina.net/noahxiao/blog/126357#OSC_h3_4">四、總結</a>
單例,大家都應該清楚,面試時也經常被問到,大家也都會寫。但就是這個最正常的東西,讓我有了新的認識。
問題是這樣的,我正準備做一個不同情況時的性能測試。Bean的生成方式就是單例,getInstance()。測試時需要測試并發下代碼的正常執行,是以采用多線程方式來調用getInstance()。這時問題來了,發現getInstance()并非真正的單例,被初始化了多次,次數不固定。Why?
經過研究發現問題是由于高并發導緻的,有很大幾率導緻多個if(instance==null)的判斷同時為true,引發重複初始化。
有問題的代碼MySingleton(X)
<code>01</code>
<code>package</code> <code>org.noahx.singleton;</code>
<code>02</code>
<code>03</code>
<code>import</code> <code>java.util.Date;</code>
<code>04</code>
<code>import</code> <code>java.util.concurrent.ExecutorService;</code>
<code>05</code>
<code>import</code> <code>java.util.concurrent.Executors;</code>
<code>06</code>
<code>import</code> <code>java.util.concurrent.TimeUnit;</code>
<code>07</code>
<code>import</code> <code>java.util.concurrent.atomic.AtomicInteger;</code>
<code>08</code>
<code>09</code>
<code>/**</code>
<code>10</code>
<code> </code><code>* Created with IntelliJ IDEA.</code>
<code>11</code>
<code> </code><code>* User: noah</code>
<code>12</code>
<code> </code><code>* Date: 4/28/13</code>
<code>13</code>
<code> </code><code>* Time: 9:37 PM</code>
<code>14</code>
<code> </code><code>* To change this template use File | Settings | File Templates.</code>
<code>15</code>
<code> </code><code>*/</code>
<code>16</code>
<code>public</code> <code>class</code> <code>MySingleton {</code>
<code>17</code>
<code>18</code>
<code> </code><code>private</code> <code>static</code> <code>MySingleton mySingleton;</code>
<code>19</code>
<code>20</code>
<code> </code><code>/**</code>
<code>21</code>
<code> </code><code>* 原子計數器</code>
<code>22</code>
<code> </code><code>*/</code>
<code>23</code>
<code> </code><code>private</code> <code>static</code> <code>AtomicInteger count=</code><code>new</code> <code>AtomicInteger(</code><code>0</code><code>);</code>
<code>24</code>
<code>25</code>
<code> </code><code>private</code> <code>MySingleton() {</code>
<code>26</code>
<code>27</code>
<code> </code><code>//模拟長時間初始化</code>
<code>28</code>
<code> </code><code>try</code> <code>{</code>
<code>29</code>
<code> </code><code>Thread.sleep(</code><code>5</code><code>);</code>
<code>30</code>
<code> </code><code>} </code><code>catch</code> <code>(InterruptedException e) {</code>
<code>31</code>
<code> </code><code>e.printStackTrace();</code>
<code>32</code>
<code> </code><code>}</code>
<code>33</code>
<code> </code><code>System.out.println(count.incrementAndGet());</code>
<code>34</code>
<code> </code><code>}</code>
<code>35</code>
<code>36</code>
<code> </code><code>public</code> <code>static</code> <code>MySingleton getInstance() {</code>
<code>37</code>
<code> </code><code>if</code> <code>(mySingleton == </code><code>null</code><code>) {</code>
<code>38</code>
<code> </code><code>mySingleton = </code><code>new</code> <code>MySingleton();</code>
<code>39</code>
<code>40</code>
<code> </code><code>return</code> <code>mySingleton;</code>
<code>41</code>
<code>42</code>
<code>43</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
<code>44</code>
<code> </code><code>ExecutorService executorService = Executors.newCachedThreadPool();</code>
<code>45</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>c = </code><code>0</code><code>; c < </code><code>20</code><code>; c++) {</code>
<code>46</code>
<code> </code><code>executorService.execute(</code><code>new</code> <code>TestRun());</code>
<code>47</code>
<code>48</code>
<code>49</code>
<code> </code><code>executorService.shutdown();</code>
<code>50</code>
<code>51</code>
<code> </code><code>executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);</code>
<code>52</code>
<code> </code><code>} </code><code>catch</code> <code>(Exception e) {</code>
<code>53</code>
<code>54</code>
<code>55</code>
<code>56</code>
<code>57</code>
<code>58</code>
<code> </code><code>public</code> <code>static</code> <code>class</code> <code>TestRun </code><code>implements</code> <code>Runnable {</code>
<code>59</code>
<code>60</code>
<code> </code><code>@Override</code>
<code>61</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code>62</code>
<code> </code><code>System.out.println(MySingleton.getInstance());</code>
<code>63</code>
<code>64</code>
<code>65</code>
<code>66</code>
<code>}</code>
有問題的輸出結果 (X)
<code>1</code>
<code>org.noahx.singleton.MySingleton</code><code>@35ab28fe</code>
<code>2</code>
<code>org.noahx.singleton.MySingleton</code><code>@86e293a</code>
<code>3</code>
<code>org.noahx.singleton.MySingleton</code><code>@7854a328</code>
<code>4</code>
<code>org.noahx.singleton.MySingleton</code><code>@7ca3d4cf</code>
<code>5</code>
<code>org.noahx.singleton.MySingleton</code><code>@67e8a1f6</code>
<code>6</code>
<code>org.noahx.singleton.MySingleton</code><code>@59e152c5</code>
<code>7</code>
<code>org.noahx.singleton.MySingleton</code><code>@5801319c</code>
<code>8</code>
<code>org.noahx.singleton.MySingleton</code><code>@366025e7</code>
<code>9</code>
<code>org.noahx.singleton.MySingleton</code><code>@6037fb1e</code>
<code>org.noahx.singleton.MySingleton</code><code>@7b479feb</code>
<code>org.noahx.singleton.MySingleton</code><code>@375212bc</code>
<code>org.noahx.singleton.MySingleton</code><code>@6d4c1103</code>
<code>org.noahx.singleton.MySingleton</code><code>@1cf11404</code>
<code>org.noahx.singleton.MySingleton</code><code>@17592174</code>
這裡可以清楚的看到,被初始化的14次(每次運作不固定),而且類也有時是不同的執行個體。
是以這樣的單例方式其實不是絕對意義上的單例。
修正後的代碼MySafetySingleton(V)
<code>import</code> <code>java.util.concurrent.locks.ReentrantLock;</code>
<code>public</code> <code>class</code> <code>MySafetySingleton {</code>
<code> </code><code>private</code> <code>static</code> <code>MySafetySingleton mySingleton;</code>
<code> </code><code>private</code> <code>static</code> <code>AtomicInteger count = </code><code>new</code> <code>AtomicInteger(</code><code>0</code><code>);</code>
<code> </code><code>* 鎖</code>
<code> </code><code>private</code> <code>static</code> <code>ReentrantLock lock = </code><code>new</code> <code>ReentrantLock();</code>
<code> </code><code>private</code> <code>MySafetySingleton() {</code>
<code> </code><code>public</code> <code>static</code> <code>MySafetySingleton getInstance() {</code>
<code> </code><code>if</code> <code>(mySingleton == </code><code>null</code><code>) { </code><code>//為了不影響以後運作的速度(非第一次)首先判定是否為null</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>lock.lock(); </code><code>//先上鎖,來保證下面這個代碼不會同時被執行</code>
<code> </code><code>if</code> <code>(mySingleton == </code><code>null</code><code>) { </code><code>//第二次判斷是否為null,這樣可以放棄由于初始并發而導緻多次執行個體的問題</code>
<code> </code><code>mySingleton = </code><code>new</code> <code>MySafetySingleton();</code>
<code> </code><code>}</code>
<code> </code><code>} </code><code>finally</code> <code>{</code>
<code> </code><code>lock.unlock();</code>
<code> </code><code>}</code>
<code>67</code>
<code>68</code>
<code>69</code>
<code>70</code>
<code>71</code>
<code>72</code>
<code>73</code>
<code> </code><code>System.out.println(MySafetySingleton.getInstance());</code>
<code>74</code>
<code>75</code>
<code>76</code>
<code>77</code>
修正後的輸出結果(V)
<code>org.noahx.singleton.MySafetySingleton</code><code>@6cf84386</code>
看似簡單的問題總是隐藏殺機,稍有不慎就會出現問題。