多線程8鎖問題

多線程8鎖問題

在多線程環境中,訪問資源類的順序到底是如何?
最近,在網絡上看到一道題目,通過多線程的8種鎖情況瞭解訪問資源類的順序。


  1. 標準訪問,先打印郵件方法還是短信方法?
class Phone{
    public synchronized void sendEmail(){
        System.out.println("----sendEmail----");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        //睡眠4毫秒
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}

運行結果:

synchronized關鍵字鎖的不是當前方法,而是該方法所在的整個資源類,也就是說,同一時間下,只能有一個線程進入當前的資源類訪問同步方法,所以運行結果跟方法調用的順序相同。


  1. 郵件方法暫停4秒,先打印郵件方法還是短信方法?
class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}

運行結果:

第二鎖的原理跟第一鎖相同,只不過是在發郵件方法中線程睡眠了4秒,當線程A訪問發郵件方法睡眠時,線程B無法進入該資源類,所以運行結果還是相同。


  1. 新增一個普通方法hello,先打印郵件方法還是短信方法?
class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public void hello(){
        System.out.println("----hello----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone.hello();
        }, "B").start();
    }
}

運行結果:

新增的hello方法並沒有被synchronized關鍵字修飾,不是同步方法,所以在線程A訪問發郵件方法時,線程B可以訪問到hello方法,而線程A需要睡眠4秒,所以運行結果是hello先。


  1. 兩個資源類,先打印郵件方法還是短信方法?
class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}

運行結果:

第四鎖中創建了兩個資源類,main方法中訪問的是兩個不同資源類的方法,相互並不干擾,而發郵件的方法睡眠4秒,所以發短信的方法會先打印。

  1. 兩個靜態同步方法,同一個資源類,先打印郵件方法還是短信方法?
class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public static synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}

運行結果:

這題涉及到static靜態,鎖的不再是這個資源類對象,鎖的是這個對象在類加載器中的類模板(.class),但由於還是同一個資源類,所以的順序原理還是跟第一鎖差不多。

  1. 兩個靜態同步方法,兩個資源類,先打印郵件方法還是短信方法?
class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public static synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}

運行結果:

第六鎖變換成兩個資源類,但由於static鎖的是整個類的類模板(.class),所以不論實例化多少個資源類訪問的結果還是相同的。

  1. 一個同步方法,一個靜態同步方法,一個資源類,先打印郵件方法還是短信方法?
class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}

運行結果:

第七鎖,一個靜態同步方法,一個普通同步方法,一個鎖的是整個類模板,一個鎖的是當前對象,兩者相互不衝突,所以發短信先顯示,發郵件的需要睡眠4秒才顯示。

  1. 一個同步方法,一個靜態同步方法,兩個資源類,先打印郵件方法還是短信方法?
class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("----sendEmail----");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS----");
    }
}
public class lock8 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        TimeUnit.MILLISECONDS.sleep(4);
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}

運行結果:

第八鎖與第七鎖的原理類似。

總結

synchronized實現同步的基礎:Java中的每個對象都可以作爲鎖。
具體表現爲以下三種形式:

  • 對於普通同步方法,鎖的是當前實例對象
  • 對於靜態同步方法,鎖的是當前類的Class對象
  • 對於同步方法快,鎖的是synchronized括號裏配置的對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章