懶漢式創建單例模式線程安全問題

一、synchronized加鎖
    

public class Student {
    
        private static Student student;

        private Student(){}

        public static synchronized Student getInstance(){
            if (student == null) {
                student = new Student();
            }

            return student;
        }
    }


    這種方法可以保證線程安全,但性能會非常差,特別是在併發情況下,當一個線程執行這個方法的時候,其他線程都會被阻塞(JDK1.6之前,1.6之後會進行自旋,利用CAS操作不斷獲取鎖,次數不一定(自適應自旋鎖),仍獲取不到鎖後纔會阻塞)。
 二、

    public class Student {
    
        private static Student student;

        private Student(){}

        public static Student getInstance(){
            if (student == null) {
                synchronized (Student.class){
                    student = new Student();
                }
            }
            return student;
        }
    }


    這種方法只有在student對象爲空時纔會創建對象,細化了鎖的力度,但在併發情況下,線程A,B同時執行這個方法,同時進行判斷,都不爲空,A線程得到鎖,初始化,B線程自旋,等A線程釋放鎖後,B線程接着進行初始化,A  B兩個線程得到的不是同一個對象。
三、

    public class Student {
    
        private static Student student;

        private Student(){}

        public static Student getInstance(){
            if (student == null) {
                synchronized (Student.class){
                    if (student == null) {
                        student = new Student();
                    }
                }
            }
            return student;
        }
    }


    那利用雙重檢查判斷會怎麼樣呢?B線程得到鎖後初始化前仍會判斷student對象是否爲空,這時student對象已經初始化了,判斷結果爲false,B不會再次創建對象。解決了線程安全問題。
四、真的是這樣麼?
    創建一個對象分爲三步
        1.分配內存空間
        2.初始化對象
        3.將內存空間的地址賦值給對象的引用。
            *其中2、3步執行時虛擬機是會重排序的。
        若A線程執行時JVM將2 3步重排序,那麼此時對象的易用指向的內存空間僅僅只是一個地址,這時候B線程進行第一次爲空判斷時,發現不爲空,會將對象的引用返回。
    既然錯誤是由指令重排序造成的,那麼我們只有禁止JVM對這個對象創建時指令重排序即可。可以使用volatile關鍵字。

    public class Student {
    
        private static volatile Student student;

        private Student(){}

        public static Student getInstance(){
            if (student == null) {
                synchronized (Student.class){
                    if (student == null) {
                        student = new Student();
                    }
                }
            }
            return student;
        }
    }

 

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