實作一個自定義的lock類
鎖是java并發api提供的基本同步機制之一。它允許程式員保護代碼的臨界區,是以,在某個時刻隻有一個線程能執行這個代碼塊。它提供以下兩種操作:
lock():當你想要通路一個臨界區時,調用這個方法。如果有其他線程正在運作這個臨界區,其他線程将阻塞,直到它們被這個鎖喚醒,進而擷取這個臨界區的通路。
unlock():你在臨界區的尾部調用這個方法,允許其他線程通路這個臨界區。
在java并發api中,鎖是在lock接口及其一些實作類中聲明的,比如reentrantlock類。
在這個指南中,你将學習如何實作你自己的lock對象,它将實作一個實作了lock接口并可用來保護臨界區的類。
準備工作…
這個指南的例子使用eclipse ide實作。如果你使用eclipse或其他ide,如netbeans,打開它并建立一個新的java項目。
如何做…
按以下步驟來實作的這個例子:
1.建立一個繼承abstractqueuedsynchronizer類的myqueuedsynchronizer類。
<code>1</code>
<code>public</code> <code>class</code> <code>myabstractqueuedsynchronizer </code><code>extends</code>
<code>2</code>
<code>abstractqueuedsynchronizer {</code>
2.聲明一個私有的、atomicinteger類型的屬性state。
<code>private</code> <code>atomicinteger state;</code>
3.實作這個類的構造器,并初始化它的屬性。
<code>public</code> <code>myabstractqueuedsynchronizer() {</code>
<code>state=</code><code>new</code> <code>atomicinteger(</code><code>0</code><code>);</code>
<code>3</code>
<code>}</code>
4.實作tryacquire()方法。這個方法試圖将變量state的值從0變成1。如果成功,它将傳回true,否則,傳回false。
<code>@override</code>
<code>protected</code> <code>boolean</code> <code>tryacquire(</code><code>int</code> <code>arg) {</code>
<code>return</code> <code>state.compareandset(</code><code>0</code><code>, </code><code>1</code><code>);</code>
<code>4</code>
5.實作tryrelease()方法。這個方法試圖将變量sate的值從1變成0.如果成功,它将傳回true,否則,傳回false。
<code>protected</code> <code>boolean</code> <code>tryrelease(</code><code>int</code> <code>arg) {</code>
<code>return</code> <code>state.compareandset(</code><code>1</code><code>, </code><code>0</code><code>);</code>
6.建立一個mylock類,并指定它實作lock接口。
<code>public</code> <code>class</code> <code>mylock </code><code>implements</code> <code>lock{</code>
7.聲明一個私有的、abstractqueuedsynchronizer類型的屬性sync。
<code>private</code> <code>abstractqueuedsynchronizer sync;</code>
8.實作這個類的構造器,并使用myabstractqueuesynchronizer對象來初始化它的sync屬性。
<code>public</code> <code>mylock() {</code>
<code>sync=</code><code>new</code> <code>myabstractqueuedsynchronizer();</code>
9.實作lock()方法。調用sync對象的acquire()方法。
<code>public</code> <code>void</code> <code>lock() {</code>
<code>sync.acquire(</code><code>1</code><code>);</code>
10.實作lockinterruptibly()方法。調用sync對象的acquireinterruptibly()方法。
<code>public</code> <code>void</code> <code>lockinterruptibly() </code><code>throws</code> <code>interruptedexception {</code>
<code>sync.acquireinterruptibly(</code><code>1</code><code>);</code>
11.實作trylock()方法。調用sync對象的tryacquirenanos()方法。
<code>public</code> <code>boolean</code> <code>trylock() {</code>
<code>try</code> <code>{</code>
<code>return</code> <code>sync.tryacquirenanos(</code><code>1</code><code>, </code><code>1000</code><code>);</code>
<code>5</code>
<code>} </code><code>catch</code> <code>(interruptedexception e) {</code>
<code>6</code>
<code>e.printstacktrace();</code>
<code>7</code>
<code>return</code> <code>false</code><code>;</code>
<code>8</code>
<code>9</code>
12.實作其他版本的帶有兩個參數的trylock()方法。一個long類型參數,名為time,一個timeunit類型參數,名為unit。調用sync對象的tryacquirenanos()方法。
<code>public</code> <code>boolean</code> <code>trylock(</code><code>long</code> <code>time, timeunit unit)</code>
<code>throws</code> <code>interruptedexception {</code>
<code>return</code> <code>sync.tryacquirenanos(</code><code>1</code><code>, timeunit.nanoseconds.</code>
<code>convert(time, unit));</code>
13.實作unlock()方法。調用sync對象的release()方法。
<code>public</code> <code>void</code> <code>unlock() {</code>
<code>sync.release(</code><code>1</code><code>);</code>
14.實作newcondition()方法。建立一個新的sync對象的内部類conditionobject。
<code>public</code> <code>condition newcondition() {</code>
<code>return</code> <code>sync.</code><code>new</code> <code>conditionobject();</code>
15.建立一個task類,并指定它實作runnable接口。
<code>public</code> <code>class</code> <code>task </code><code>implements</code> <code>runnable {</code>
16.聲明一個私有的、mylock類型的屬性lock。
<code>private</code> <code>mylock lock;</code>
17.聲明一個私有的、string類型的屬性name。
<code>private</code> <code>string name;</code>
18.實作這個類的構造器,并初始化它的屬性。
<code>public</code> <code>task(string name, mylock lock){</code>
<code>this</code><code>.lock=lock;</code>
<code>this</code><code>.name=name;</code>
19.實作這個類的run()方法。擷取鎖,令線程睡眠2秒,然後,釋放這個lock對象。
<code>01</code>
<code>02</code>
<code>public</code> <code>void</code> <code>run() {</code>
<code>03</code>
<code>lock.lock();</code>
<code>04</code>
<code>system.out.printf(</code><code>"task: %s: take the lock\n"</code><code>,name);</code>
<code>05</code>
<code>06</code>
<code>timeunit.seconds.sleep(</code><code>2</code><code>);</code>
<code>07</code>
<code>system.out.printf(</code><code>"task: %s: free the lock\n"</code><code>,name);</code>
<code>08</code>
<code>09</code>
<code>10</code>
<code>} </code><code>finally</code> <code>{</code>
<code>11</code>
<code>lock.unlock();</code>
<code>12</code>
<code>13</code>
20.實作這個例子的主類,通過建立main類,并實作main()方法。
<code>public</code> <code>class</code> <code>main {</code>
<code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>
21.建立一個mylock對象,名為lock。
<code>mylock lock=</code><code>new</code> <code>mylock();</code>
22.建立和執行10個task任務。
<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<</code><code>10</code><code>; i++){</code>
<code>task task=</code><code>new</code> <code>task(</code><code>"task-"</code><code>+i,lock);</code>
<code>thread thread=</code><code>new</code> <code>thread(task);</code>
<code>thread.start();</code>
23.使用trylock()方法嘗試擷取鎖。等待1秒,如果你沒有擷取鎖,寫入一條資訊并重新嘗試。
<code>boolean</code> <code>value;</code>
<code>do</code> <code>{</code>
<code>value=lock.trylock(</code><code>1</code><code>,timeunit.seconds);</code>
<code>if</code> <code>(!value) {</code>
<code>system.out.printf(</code><code>"main: trying to get the lock\n"</code><code>);</code>
<code>value=</code><code>false</code><code>;</code>
<code>} </code><code>while</code> <code>(!value);</code>
24.寫入一條資訊表明你已擷取鎖,然後釋放它。
<code>system.out.printf(</code><code>"main: got the lock\n"</code><code>);</code>
25.寫入一條資訊表明程式的結束。
<code>system.out.printf(</code><code>"main: end of the program\n"</code><code>);</code>
它是如何工作的…
java并發api提供一個類,可以用來實作擁有鎖和信号量特征的同步機制。它就是abstractqueuedsynchronizer,正如其名,它是一個抽象類。它提供控制臨界區的通路和管理正在阻塞等待通路臨界區的線程隊列的操作。這些操作是基于以下兩個抽象方法:
tryacquire():嘗試通路臨界區時,調用這個方法。如果線程調用這個方法可以通路臨界區,那麼這個方法傳回true,否則,傳回false。
tryrelease():嘗試翻譯臨界區的通路,調用這個方法。如果線程調用這個方法可以釋放臨界區的通路,那麼這個方法傳回true,否則,傳回false.
在這些方法中,你已實作可用來控制臨界區通路的機制。在你的例子中,你已實作繼承abstractqueuedsyncrhonizer類的myqueuedsynchonizer類,并使用atomicinteger變量實作抽象方法來控制臨界區的通路。如果鎖是自由的,這個變量的值為0,表明線程可以通路這個臨界區。如果鎖是阻塞的,這個變量的值為1,表明線程不能通路這個臨界區。
你已使用atomicinteger類提供的compareandset()方法,嘗試将你指定的值作為第一個參數改變成你指定的值作為第二個參數。實作tryacquire()方法,你嘗試将原子變量的值從0變成1。同樣地,你實作tryrelease()方法,嘗試将原子變量的值從1變成0。
你必須實作這個類,因為abstractqueuedsynchronizer類的其他實作(比如,所使用的reentrantlock類)是作為私有的内部類使用來實作的,是以你不能通路它。
然後,你已實作mylock類。這個類實作lock接口,有一個myqueuedsynchronizer對象屬性。你已使用myqueuedsynchronizer對象的方法,來實作lock接口的所有方法。
最後,你實作了task類,它實作了runnable接口,并使用一個mylock對象來控制臨界區的通路。這個臨界區令線程睡眠2秒。主類建立一個mylock對象,并運作10個task對象來共享這把鎖。主類也使用trylock()方法來嘗試擷取鎖的通路。
當你執行這個例子,你可以看到隻有一個線程可以通路這個臨界區,并且當這個線程結束,其他線程可以繼續通路這個臨界區。
你可以使用你自己的鎖來寫入關于它的使用的日志資訊,控制鎖定時間,或實作先進的同步機制來控制。比如,隻能在特定的時間内,才能對資源通路。
不止這些…
abstractqueuedsynchronizer類提供兩個方法可以用來控制鎖的狀态,它們就是getstate()和setstate()方法。這兩個方法,接收和傳回一個整數值作為鎖的狀态。你可以使用這兩個方法而不是atomicinteger屬性來存儲鎖的狀态。
java并發api提供其他類來實作同步機制。它就是abstractqueuedlongsynchronizer類,它與abstractqueuedsynchronizer一樣,除了使用一個long類型屬性來存儲線程的狀态。
參見
<a href="http://ifeve.com/basic-thread-synchronization-5/">在第2章,基本線程同步中的使用locks同步代碼塊指南</a>