關於java 多線程編程的同步問題

      說到線程編程,我們初步的認識,就是通過編程的方式讓系統資源在不同的線程中切換,從而實現系統高效的運行。然後這看似簡單明瞭的問題,實際操作起來,卻隱藏了很多複雜的細節,其中線程間的同步和通信問題,就是導致多線程編程出現各種異常的關鍵。舉一個簡單的例子,銀行的有多個服務窗口,每一個服務窗口通過叫號提供服務。假設系統設計了一個變量number,代表當前的的號數,當number大於10的時候,就不再叫號。現在假設有三個窗口線程1,2,3.線程1執行到①的時候,判斷number的值正好等於10,準備執行提供服務②的時候,系統分配的時間資源用完切換到了線程2,此時線程1所保存的斷點是②處的地址信息。線程2運行到判斷語句①,發現number仍然是10滿足條件,進入提供服務②,並且將number加一,當系統又切換到線程1的時候,從斷點②處開始,並不經過判斷,就執行了提供服務,而實際的number值是11,不應該被提供服務。這就是一個線程的同步問題,我們應該通過一種機制保證這種現象不發生,在java中有一個叫同步鎖synchronized的機制保證了對於臨界區域在同一時間段只允許一個線程的操作。

if(number<=10) .......①

{

       提供服務;     .........②

       number++;  .........③

}

     現在我們看一個加入了同步鎖的方式所寫的一個銀行服務窗口程序,該程序例子,是我從一本書上選取的,我自己修改擴展了一下。

class TicketWindow3 implements Runnable
{
private static int max_value = 0;
private boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(TicketWindow3.class)
{
if(max_value > 500)
{
break;
}
try
{
Thread.sleep(10);
}catch (InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":lock..."+max_value++);
}
}
}
else
{
while(true)
if(ticket())
break;
}
}
private synchronized static boolean ticket()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
public void change() throws InterruptedException
{
Thread.sleep(30);
this.flag = false;
}
}
public class bank3 {
public static void main(String[] args) throws InterruptedException {
TicketWindow3 tw3 = new TicketWindow3();
Thread t1 = new Thread(tw3);
Thread t2 = new Thread(tw3);
t1.start();
tw3.change();
t2.start();
}
}

      在該例子中,對臨界區域進行了加鎖,而TicketWindow3類中,通過了兩種方式加鎖,在測試這兩種方式加鎖的區域是同一個,能夠實現在一個線程佔用臨界區域,其他線程不能再訪問。第一種加鎖方式,是通過synchronized鎖住類,即所謂的this鎖,而第二種方式是通過synchronized鎖住一個類方法ticket(),單看這個程序,我們並不能一定可以確定第二種方式鎖住類方法也是this鎖,因爲還有一種假設的解釋,鎖住類方法只是鎖住該類中的一個部分,而第一種鎖住this的範圍更大,這兩種方式是包含的關係,雖然最後結果的確實現了線程的同步,保證了最後結果的正確,但並不能說明問題。因此我對該程序進行了一個修改,使得類中具有兩個類方法的的鎖,ticket1()和ticket2(),而不加this鎖。

class TicketWindow3 implements Runnable
{
private static int max_value = 0;
private boolean flag1 = true;
private boolean flag2 = true;
public void run()
{
if(flag1)
{
while(true)
{
synchronized(TicketWindow3.class)
{
if(max_value > 500)
{
break;
}
try
{
Thread.sleep(10);
}catch (InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":lock..."+max_value++);
}
}
}
else
{
if(flag2)
{
while(true)
{
if(ticket1())
break;
}
}
else
{
while(true)
{
if(ticket2())
break;
}
}
   }
}
private synchronized static boolean ticket1()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
private synchronized static boolean ticket2()
{
if (max_value > 500)
{
return true;
}
try
{
Thread.sleep(10);
}catch(InterruptedException e)
{

}
System.out.println(Thread.currentThread().getName()+":method.."+max_value++);
return false;
}
public void change() throws InterruptedException
{
Thread.sleep(30);
if(flag1)
   flag1 = false;
else
flag2 = false;
}
}
public class bank3 {
public static void main(String[] args) throws InterruptedException {
TicketWindow3 tw3 = new TicketWindow3();
tw3.change();
Thread t1 = new Thread(tw3);
Thread t2 = new Thread(tw3);
Thread t3 = new Thread(tw3);
t1.start();
t2.start();
tw3.change();
t3.start();
}
}


從結果中我們可以看到,通過對兩個不同的類方法加鎖,依然可以保證線程的同步。這就說明上述的假設類方法是類中更小區域的鎖,是不對的,類方法的鎖也是this鎖。

發佈了28 篇原創文章 · 獲贊 35 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章