天天看點

愛上多線程——重複初始化問題

<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 &lt; </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>

看似簡單的問題總是隐藏殺機,稍有不慎就會出現問題。

繼續閱讀