<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官真是搞笑!讓實現(xiàn)線程安全的單例,又不讓使用synchronized!

          共 3778字,需瀏覽 8分鐘

           ·

          2020-05-12 23:22

          單例模式,是Java中比較常見的一個設計模式,也是我在面試時經常會問到的一個問題。

          經過我的初步統(tǒng)計,基本上有60%左右的人可以說出2-4種單例的實現(xiàn)方式,有40%左右的人可以說出5-6種單例的實現(xiàn)方式,只有20%左右的人能夠說出7種單例的實現(xiàn)。
          而只有不到1%的人能夠說出7種以上的單例實現(xiàn)。
          其實,作為面試官,我大多數(shù)情況下之所以問單例模式,是因為這個題目可以問到很多知識點。
          比如線程安全、類加載機制、synchronized的原理、volatile的原理、指令重排與內存屏障、枚舉的實現(xiàn)、反射與單例模式、序列化如何破壞單例、CAS、CAS的ABA問題、Threadlocal等知識。
          一般情況下,只需要從單例開始問起,大概就可以完成一場面試的整個流程,把我想問的東西都問完,可以比較全面的了解一個面試者的水平。
          以下,是一次面試現(xiàn)場的還原,從單例模式開始:
          Q:你知道怎么不使用synchronized和lock實現(xiàn)一個線程安全的單例嗎?A:我知道,可以使用"靜態(tài)內部類"實現(xiàn)。
          靜態(tài)內部類實現(xiàn)單例模式:

          public class Singleton {  private static class SingletonHolder {  private static final Singleton INSTANCE = new Singleton();      }  private Singleton (){}  public static final Singleton getInstance() {  return SingletonHolder.INSTANCE;      }  }

          Q:除了靜態(tài)內部類還會其他的方式嗎?A:還有就是兩種餓漢模式。
          餓漢實現(xiàn)單例模式:
          public class Singleton {      private static Singleton instance = new Singleton();      private Singleton (){}      public static Singleton getInstance() {      return instance;      }  }
          ? ??
          餓漢變種實現(xiàn)單例模式:
          public class Singleton {      private Singleton instance = null;      static {      instance = new Singleton();      }      private Singleton (){}      public static Singleton getInstance() {      return this.instance;      }  }

          Q:那你上面提到的幾種都是線程安全的嗎?A:是線程安全的Q:那是如何做到線程安全的呢?A:應該是因為我使用了static,然后類加載的時候就線程安全了吧?Q:其實你說的并不完全對,因為以上幾種雖然沒有直接使用synchronized,但是也是間接用到了。(這里面根據回答情況會朝兩個不同的方向展開:1、類加載機制、模塊化等;2、繼續(xù)深入問單例模式)
          類加載過程的線程安全性保證

          以上的靜態(tài)內部類、餓漢等模式均是通過定義靜態(tài)的成員變量,以保證單例對象可以在類初始化的過程中被實例化。


          這其實是利用了ClassLoader的線程安全機制。ClassLoader的loadClass方法在加載類的時候使用了synchronized關鍵字。

          所以, 除非被重寫,這個方法默認在整個裝載過程中都是線程安全的。所以在類加載過程中對象的創(chuàng)建也是線程安全的。


          Q:那還回到剛開始的問題,你知道怎么不使用synchronized和lock實現(xiàn)一個線程安全的單例嗎?(并不是故意窮追不舍,而是希望能可以引發(fā)面試者的更多思考)A:額、、、那枚舉吧,枚舉也可以實現(xiàn)單例。
          枚舉實現(xiàn)單例模式:

          public enum Singleton {      INSTANCE;      public void whateverMethod() {      }  }

          Q:那你知道枚舉單例的原理嗎?如何保證線程安全的呢?


          枚舉單例的線程安全問題

          枚舉其實底層是依賴Enum類實現(xiàn)的,這個類的成員變量都是static類型的,并且在靜態(tài)代碼塊中實例化的,和餓漢有點像, 所以他天然是線程安全的。


          Q:所以,枚舉其實也是借助了synchronized的,那你知道哪種方式可以完全不使用synchronized的嗎?A:en....我想想Q:(過了一會他好像沒有思路)你知道CAS嗎?使用CAS可以實現(xiàn)單例嗎?(面試中,如果面試者對于鎖比較了解的話,那我大多數(shù)情況下都會繼續(xù)朝兩個方向深入問:1、鎖的實現(xiàn)原理;2、非鎖,如CAS、ThreadLocal等)A:哦,我知道,CAS是一項樂觀鎖技術,當多個線程嘗試使用CAS同時更新一個變量時,只有其中一個線程能更新成功。
          借助CAS(AtomicReference)實現(xiàn)單例模式:

          public class Singleton {private static final AtomicReference INSTANCE = new AtomicReference();
          private Singleton() {}
          public static Singleton getInstance() {for (;;) { Singleton singleton = INSTANCE.get();if (null != singleton) {return singleton; }
          singleton = new Singleton();if (INSTANCE.compareAndSet(null, singleton)) {return singleton; } } }}
          Q:使用CAS實現(xiàn)的單例有沒有什么優(yōu)缺點呀?A:用CAS的好處在于不需要使用傳統(tǒng)的鎖機制來保證線程安全,CAS是一種基于忙等待的算法,依賴底層硬件的實現(xiàn),相對于鎖它沒有線程切換和阻塞的額外消耗,可以支持較大的并行度。Q:你說的好像是優(yōu)點?那缺點呢?
          CAS實現(xiàn)的單例的缺點

          CAS的一個重要缺點在于如果忙等待一直執(zhí)行不成功(一直在死循環(huán)中),會對CPU造成較大的執(zhí)行開銷。


          另外,代碼中,如果N個線程同時執(zhí)行到 singleton = new Singleton();的時候,會有大量對象被創(chuàng)建,可能導致內存溢出。

          Q:好的,除了使用CAS以外,你還知道有什么辦法可以不使用synchronized實現(xiàn)單例嗎?A:這回真的不太知道了。Q:(那我再提醒他一下吧)可以考慮下ThreadLocal,看看能不能實現(xiàn)?(面試者沒有思路的時候,我?guī)缀醵紩茸鲆幌绿嵝?,實在沒有思路再換下一個問題)A:ThreadLocal?這也可以嗎?Q:你先說下你理解的ThreadLocal是什么吧(通過他的回答,貌似對這個思路有些疑惑,不著急。先問一個簡單的問題,讓面試者放松一下,找找自信,然后再繼續(xù)問)
          ThreadLoacal

          ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數(shù)據的訪問沖突。對于多線程資源共享的問題,同步機制(synchronized)采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。


          同步機制僅提供一份變量,讓不同的線程排隊訪問,而ThreadLocal為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

          Q:那理論上是不是可以使用ThreadLocal來實現(xiàn)單例呢?A:應該也是可行的。
          使用ThreadLocal實現(xiàn)單例模式:

          public class Singleton {private static final ThreadLocal singleton =new ThreadLocal() {@Overrideprotected Singleton initialValue() {return new Singleton();         }     };public static Singleton getInstance() {return singleton.get();     }
          private Singleton() {}}

          Q:嗯嗯,好的,那有關單例模式的實現(xiàn)的問題我就問的差不多了。(ThreadLocal這種寫法主要是考察面試者對于ThreadLocal的理解,以及是否可以把知識活學活用,但是實際上,這種所謂的"單例",其實失去了單例的意義...)(但是說實話,能回答到這一題的人很少,大多數(shù)面試者基本上在前面幾道題就已經沒有思路了,大多數(shù)情況下根本不會問到這個問題就要改方向了)A:(心中竊喜)嗯嗯,學習到很多,感謝Q:那...你知道如何破壞單例嗎?(單例問題,必問的一個。通過這個引申到序列化和反射的相關知識)A:(額....)

          長按關注,還原真實面試現(xiàn)場

          瀏覽 119
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  狠狠撸电影网 | 一级黄色视频网站 | 国产各种高潮视频在线播放 | 韩国在线免费观看日ccc | 中文字幕国产第一页 |