SynchronizedMonitor總結

同步鎖

Monitor

  • monitor指與synchronized關聯的同步資源所關聯的鎖
  • monitor有一個計數器,初始化爲0,如果monitor的計數器爲0,則意味着該monitor的lock還沒有被獲得,某個線程獲得之後將立即對該計數器加一,從此該線程就是這個monitor的所有者了
  • 如果一個已經擁有該monitor所有權的線程重入(重新調用該資源),monitor的計數器會再次累加
  • 如果monitor已經被其他線程所擁有,則其他線程嘗試獲取該monitor的所有權時,會被陷入阻塞狀態知道monitor計數器變爲0,才能再次嘗試獲取對monitor的擁有權

使用synchronized需要注意的問題

  1. 與monitor關聯的對象不能爲空

    private final Object mutex=null;
    public void synMethod(){
        synchronized(mutex){
            //TODO
        }
    }
    
  2. synchronized關鍵字作用域不應該太大

    public static class Task implements Runnable{
        public synchronized void run(){
            //TODO
        }
    }
    

    ​ 上面的代碼對整個線程的執行域也就是run方法都進行了synchronized同步,從而喪失了併發的能力,synchronized關鍵字應該儘可能的只作用於共享資源的讀寫作用域

  3. 不同的minitor企圖鎖相同的方法

    //模擬排隊取號
    package online.hengtian.Thread;
    
    import java.util.Arrays;
    import java.util.List;
    class Run implements Runnable{
        private int index=1;
        private final static int MAX=500;
        private final static Object obj=new Object();
    
        @Override
        public void run() {
            synchronized (obj) {
                while (index <= MAX) {
                    System.out.println(Thread.currentThread() + " 的號碼是:" + index++);
                }
            }
        }
    }
    public class TicketWindow {
        public static void main(String[] args){
            List<String> windows= Arrays.asList(
                    "一號窗口","二號窗口","三號窗口","四號窗口"
            );
    /*****************錯誤示例開始*********************************/
            windows.stream()
                    .map(t->new Thread(new Run(),t))
                    .forEach(Thread::start);
    /*****************錯誤示例結束*********************************/
            windows= Arrays.asList(
                    "一號窗口","二號窗口","三號窗口","四號窗口"
            );
            Runnable r=new Run();
            windows.stream()
                    .map(t->new Thread(r,t))
                    .forEach(Thread::start);
        }
    }
    
    

    ​ 在上面所示的錯誤示例中,構造了四個Runnable實例,從而讓每個線程都擁有了自己的monitor,起不到互斥的作用,在錯誤實例的下面也給出了正確的做法

  4. 多個鎖交叉導致死鎖

    private final Object READ=new Object();
    private final Object WRITE=new Object();
    public void read(){
        synchronized(READ){
            synchronized(WRITED){
                //TDDO
            }
        }
    }
    public void write(){
        synchronized(WRITE){
            synchronized(READ){
                //TDDO
            }
        }
    }    
    

This Monitor和Class Monitor的詳細介紹

This Monitor

​ synchronized關鍵字修飾的同一個實例對象的不同方法時,線程爭搶的是同一個Monitor的lock,也就是this的Monitor,通過代碼二可以驗證

//代碼一
package online.hengtian.Thread;

import java.util.concurrent.TimeUnit;

public class MonitorDemo {
    public synchronized void Method1(){
        System.out.println("Now here is method1");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method1 Termined");
    }
    public synchronized void Method2(){
        System.out.println("Now here is method2");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method2 Termined");
    }
    public static void main(String[] args){
        MonitorDemo demo=new MonitorDemo();
        new Thread(demo::Method1,"t1").start();
        new Thread(demo::Method2,"t2").start();
    }
}

//代碼二
package online.hengtian.Thread;

import java.util.concurrent.TimeUnit;

public class MonitorDemo {
    public synchronized void Method1(){
        System.out.println("Now here is method1");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method1 Termined");
    }
    public void Method2(){
        synchronized (this) {
            System.out.println("Now here is method2");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Method2 Termined");
        }
    }
    public static void main(String[] args){
        MonitorDemo demo=new MonitorDemo();
        new Thread(demo::Method1,"t1").start();
        new Thread(demo::Method2,"t2").start();
    }
}

​ 代碼一是採用的同步方法的方式,代碼而是用this的monitor,運行後效果一樣,都是運行完方法一之後再運行方法二

Class Monitor

//代碼一
package online.hengtian.Thread;

import java.util.concurrent.TimeUnit;

public class ClassMonitor {
    public static synchronized void Method1(){
        System.out.println("Now here is method1");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method1 Termined");
    }
    public static synchronized void Method2(){
        System.out.println("Now here is method2");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method2 Termined");
    }
    public static void main(String[] args){
        new Thread(ClassMonitor::Method1,"t1").start();
        new Thread(ClassMonitor::Method2,"t2").start();
    }
}
//代碼二
package online.hengtian.Thread;

import java.util.concurrent.TimeUnit;

public class ClassMonitor {
    public static synchronized void Method1(){
        System.out.println("Now here is method1");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method1 Termined");
    }
    public static void Method2(){
        synchronized(ClassMonitor.class){
            System.out.println("Now here is method2");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Method2 Termined");
        }
    }
    public static void main(String[] args){
        new Thread(ClassMonitor::Method1,"t1").start();
        new Thread(ClassMonitor::Method2,"t2").start();
    }
}

​ 代碼一和二的運行結果一致,都是先運行方法一之後執行方法二,從而可以說明,在同一個類中的靜態方法同步化時,競爭的是class的monitor

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