1. 程式人生 > >Java多執行緒6 中同步函式的鎖和同步程式碼塊的鎖的區別

Java多執行緒6 中同步函式的鎖和同步程式碼塊的鎖的區別

同步程式碼塊的出現是解決了多執行緒的安全問題,但是它增加了程式碼的縮排層級同時降低了效率(每次無論是不是對的鎖,每個路徑都要去判斷)

針對同步出現的這兩個問題,首先討論第一個。因此引出一個新的知識點————————

同步函式

關於同步函式的使用(一買車票的程式碼為例子)程式碼:

package Thread;
class Tickets implements Runnable{
    private int ticket=100;
    Object obj = new Object();
    public void run(){
        while(ticket!=0) {//多執行緒裡要有迴圈
                sale();
        }
    }
    public synchronized void sale(){
        if (ticket > 0) {
            //使CPU在這裡停一下,使用sleep()函式,Thread 中的靜態函式。使其出現錯誤的資料
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }//沒有解決方案
            System.out.println(Thread.currentThread().getName() + " " + --ticket);
        }
    }
}

public class ThreadSellTickets {
    public static void main(String[] args){
        Tickets tc = new Tickets();
        Thread T1  =new Thread(tc);
        Thread T2  =new Thread(tc);
       // Thread T3  =new Thread(tc);
        //Thread T4  =new Thread(tc);
        T1.start();
        T2.start();
        //T3.start();
        //T4.start();
    }
}

同步函式:用synchronized  修飾的函式,因為函式是被物件呼叫的,所以在使用同步函式時:this.函式名。

在函式呼叫時其實最完整的其實就是this.函式名

驗證寫法就是方法:(驗證同步函式的所是否為this)

開啟兩條執行緒,一條執行同步程式碼塊,一條執行同步函式,如果執行結果沒有錯誤,證明所用的物件/鎖  是一致的,如果執行出現了錯誤證明所用的物件不一致

程式碼:(完整正確的程式碼)

package Thread;
class Ticket implements Runnable{
    private int ticket=100;
    boolean flag=true;
    Object obj = new Object();
    public void run(){
       if(flag){
           for(int x=1;x<=5;x++){
               synchronized (this){
                   if(ticket>0){
                       try{Thread.sleep(10);}catch(InterruptedException e){}
                       System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
                   }

               }
           }
       }
       else{
           for(int y=1;y<=5;y++) {
               sale();
           }
       }
    }
    public synchronized  void sale(){
        if(ticket>0){
            try{Thread.sleep(10);}catch(InterruptedException e){}
            System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
        }
    }
}
public class Differents {
    public static void main(String[] args){
        Ticket t = new Ticket();
        Thread T1 =new Thread(t);
        Thread T2 =new Thread(t);
        T1.start();
        try{Thread.sleep(10);}catch(InterruptedException e){}//使主執行緒停一會此時只有執行緒0 在執行
        t.flag=false;
        T2.start();
    }
}

關於這段程式碼的解釋:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

首先明確CPU切換的意思:CPU是切換到執行緒,CPU切換到那個執行緒,哪一個執行緒就往下執行(指哪裡,哪裡跑)

解釋執行緒開啟間加入休眠函式,避免執行緒0開啟後flag 立刻被改變,並且主執行緒開啟執行緒1

這段程式碼run 函式中換成無限迴圈更好理解這段程式碼:

flag = true ; 執行緒0 會一直執行同步程式碼塊,執行緒0 執行一段時間後,flag 被改變,執行緒1被開啟,執行緒1執行的是同步函式。

如果兩個鎖不一樣,當執行緒0執行同步程式碼塊中程式碼到一半時CPU切換到執行緒1,執行緒1 可以在同步函式中對共享資料進行修改——資料會出現錯誤。

如果兩個鎖(為this )是一樣的,執行緒0,執行同步程式碼塊執行到一半時CPU切換到執行緒1,執行緒一無法進入同步函式,因為鎖物件還線上程0那

程式碼同步程式碼塊和同步函式的異同:

同:都是為同步服務,為了解決多執行緒的安全問題

異:鎖不一樣

同步程式碼塊的物件/鎖 是自定義的任意物件
,通過鎖來區別同步。是更加常見的

       同步函式的物件/鎖   是固定的 this 。因此只需要一個同步時可以使用同步函式

靜態同步函式使用的鎖不是this 而是類所對應的.class 檔案(位元組碼檔案物件):類名.class (按照萬物皆物件的原則這個檔案其實也是一個物件)

解釋:靜態是隨著類的載入而載入的,靜態是被類名呼叫的。類載入後的.class 檔案其實就是他的一個物件