Java線程(三):同步與鎖

參考網址(寫得非常好,珍藏網址之一):http://www.blogjava.net/tscfengkui/archive/2010/11/10/337709.html?opt=admin

當兩個以上的線程對同一個對象進行操作時,這個對象的屬性就難以控制了,會出現很多意外的結果。

舉一個在多線程操作當中經典的例子,電影院售票問題。

電影院的窗口可以同時售票,售出的票不能重疊,即不能售出兩張11排11號的位置。
    
eg1(非常經典的一段代碼):
class Source implements Runnable {
     private int ticket = 10;
     @Override
     public void run() {
            // TODO Auto-generated method stub
            for( int i = 0; i < 50; i++){
                 if( ticket>0){
                      try {
                           Thread. sleep(1000);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                     System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票");
                }
           }
     }
}

public class test9 {

     public static void main(String[] args) {
           
            //只能新建一個source,確保三個線程都是對同一個source操作
           Source source = new Source();
           
            new Thread(source, "A").start();
            new Thread(source, "B").start();
            new Thread(source, "C").start();
     }

}



可以看到C、B都售出標記爲1號的票,因爲線程C售出1號的票後,沒來得及自減,就被線程B訪問了!這明顯不符合業務的邏輯。

我們需要這樣一套機制,當資源正在被某一線程訪問的時候,資源被鎖住,其他線程訪問不了,直到被訪問的線程退出使用該資源。

原理圖:


很幸運的是,Java已經幫我們實現了這個機制,只要使用synchronized關鍵字就完美解決了這個問題。程序員不用去管Java內部怎麼實現這個機制的,只要使用它就可以了。

synchronized的用法:

1、放方法名前形成同步方法

eg2:
class Source implements Runnable {
     private int ticket = 10;
     @Override
     public void run() {
            // TODO Auto-generated method stub
            for( int i = 0; i < 50; i++){
                      try {
                           Thread. sleep(1000);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                     sale();
                }
     }
     
     public synchronized void sale() {
            if( ticket>0){
                 System.out.println(Thread.currentThread().getName()+ "號窗口售出" + "第" + ticket -- + "張票" );
           }
     }
}

結果:

2、構成同步塊
class Source implements Runnable {
      private int ticket = 10;
      @Override
      public void run() {
            // TODO Auto-generated method stub
            for( int i = 0; i < 50; i++){
                      try {
                           Thread. sleep(1000);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                      synchronized ( this) {
                            if( ticket>0){
                           
                          System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
                           }
                     }    
           }
     }
}

結果:


VIP:synchronized 的位置非常關鍵,在不同的地方有不同的效果,以下幾個例子,讀者自行分析。

eg4:

class Source implements Runnable {
      private int ticket = 10;
      @Override
      public void run() {
            // TODO Auto-generated method stub
            for( int i = 0; i < 50; i++){
                 if( ticket>0){
                      try {
                           Thread. sleep(1000);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                      synchronized( this){
                           
                          System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
                     }
                }
           }
     }
}

結果:

eg5:

class Source implements Runnable {
      private int ticket = 10;
      @Override
      public synchronized void run() {
            // TODO Auto-generated method stub
            for( int i = 0; i < 50; i++){
                 if( ticket>0){
                      try {
                           Thread. sleep(1000);
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
                           
                          System. out.println(Thread. currentThread().getName()+ "號窗口售出" + "第" + ticket-- + "張票" );
                }
           }
     }
}

結果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章