天天看點

Bug:StampedLock的中斷問題導緻CPU爆滿

stampedlock作為java8中出現的新型鎖,很可能在大多數場景都可以替代reentrantreadwritelock。它對于讀/寫都提供了四個接口(換成write為寫鎖):

readlock()

tryreadlock()

tryreadlock(long time, timeunit unit)

readlockinterruptibly()

這幾個方法對應的語義為:

擷取讀鎖(阻塞,不響應中斷)

擷取讀鎖(立即)

限時擷取讀鎖(響應中斷)

擷取讀鎖(阻塞,響應中斷)

然而在readlock方法(即不響應中斷)中存在問題(write的版本也是),觀察cpu使用率,執行以下代碼:

<code>01</code>

<code>public</code> <code>class</code> <code>teststampedlock {</code>

<code>02</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args)</code><code>throws</code> <code>interruptedexception{</code>

<code>03</code>

<code>    </code><code>final</code> <code>stampedlock lock =</code><code>new</code> <code>stampedlock();</code>

<code>04</code>

<code>    </code><code>new</code> <code>thread(){</code>

<code>05</code>

<code>       </code><code>public</code> <code>void</code> <code>run(){</code>

<code>06</code>

<code>       </code><code>long</code> <code>readlong = lock.writelock();</code>

<code>07</code>

<code>       </code><code>locksupport.parknanos(6100000000l);</code>

<code>08</code>

<code>       </code><code>lock.unlockwrite(readlong);</code>

<code>09</code>

<code>     </code><code>}</code>

<code>10</code>

<code>    </code><code>}.start();</code>

<code>11</code>

<code>    </code><code>thread.sleep(</code><code>100</code><code>);</code>

<code>12</code>

<code>    </code><code>for</code><code>(</code><code>int</code> <code>i =</code><code>0</code><code>; i &lt;</code><code>3</code><code>; ++i)</code>

<code>13</code>

<code>       </code><code>new</code> <code>thread(</code><code>new</code> <code>occupiedcpureadthread(lock)).start();</code>

<code>14</code>

<code>    </code><code>}</code>

<code>15</code>

<code>    </code><code>private</code> <code>static</code> <code>class</code> <code>occupiedcpureadthread</code><code>implements</code> <code>runnable{</code>

<code>16</code>

<code>        </code><code>private</code> <code>stampedlock lock;</code>

<code>17</code>

<code>        </code><code>public</code> <code>occupiedcpureadthread(stampedlock lock){</code>

<code>18</code>

<code>            </code><code>this</code><code>.lock = lock;</code>

<code>19</code>

<code>        </code><code>}</code>

<code>20</code>

<code>        </code><code>public</code> <code>void</code> <code>run(){</code>

<code>21</code>

<code>            </code><code>thread.currentthread().interrupt();</code>

<code>22</code>

<code>            </code><code>long</code> <code>lockr = lock.readlock();</code>

<code>23</code>

<code>            </code><code>system.out.println(thread.currentthread().getname() +</code><code>" get read lock"</code><code>);</code>

<code>24</code>

<code>            </code><code>lock.unlockread(lockr);</code>

<code>25</code>

<code>26</code>

<code>27</code>

<code>}</code>

先開啟一個線程擷取寫鎖并保持6秒,再開啟三個帶着中斷狀态的線程去擷取讀鎖(readlock方法),結果是3個核心被占據了近6秒。

原因在于沒有使用儲存/複原中斷狀态的機制,通過hack源碼,插入儲存中斷和傳回前恢複中斷的相關代碼即可修複:

<code>1</code>

<code>boolean</code> <code>interrupted = </code><code>false</code><code>;</code>

<code>if</code><code>(interrupted)</code>

<code>2</code>

<code>    </code><code>thread.currentthread().interrupt();</code>

<code>3</code>

<code>return</code> <code>ns;</code>

<code>if</code><code>(thread.interrupted()){</code>

<code>    </code><code>if</code><code>(interruptible)</code>

<code>        </code><code>return</code> <code>cancelwaiter(node, p,</code><code>true</code><code>);</code>

<code>4</code>

<code>    </code><code>else</code>

<code>5</code>

<code>        </code><code>interrupted =</code><code>true</code><code>;</code>

<code>6</code>