<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)方式

          共 9595字,需瀏覽 20分鐘

           ·

          2021-05-05 12:13

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質文章,第一時間送達

          76套java從入門到精通實戰(zhàn)課程分享

          什么是線程安全

          一個類可以被多個線程安全調用那么這個類就是線程安全的。


          根據(jù)線程共享數(shù)據(jù)的安全程度可以分為以下五類線程安全:

          (1)不可變

          (2)絕對線程安全

          (3)相對線程安全

          (4)線程兼容

          (5)線程對立

          結尾有彩蛋哦


          不可變(Immutable)

          不可變(Immutable)的對象一定是線程安全的,不需要再采取任何的線程安全保障措施。只要一個不可變的對象被正確地構建出來,永遠也不會看到它在多個線程之中處于不一致的狀態(tài)。


          不可變的類型:

          • final 關鍵字修飾的基本數(shù)據(jù)類型

          • String

          • 枚舉類型

          • Number 部分子類,如 Long 和 Double等數(shù)值包裝類型,BigInteger 和 BigDecimal 等大數(shù)據(jù)類型。但同為 Number 的原子類 AtomicInteger 和 AtomicLong 則是可變的。

          • 集合類型,可以使用 Collections.unmodifiableXXX() 方法來獲取一個不可變的集合。

          獲取不可變集合代碼如下:

          public class ImmutableExample {
              public static void main(String[] args) {
                  Map<String, Integer> map = new HashMap<>();
                  Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);
                  unmodifiableMap.put("a", 1);
              }
          }

          由于 unmodifiableMap是不可變的,因此使用put方法時會報出 UnsupportedOperationException異常


          絕對線程安全

          不管運行時環(huán)境如何,調用者都不需要任何額外的同步措施這就是絕對線程安全。


          相對線程安全

          相對線程安全需要保證對這個對象單獨的操作是線程安全的,在調用的時候不需要做額外的保障措施。但是對于一些特定順序的連續(xù)調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。


          java里大部分的線程安全類就是相對線程安全的,例如 Vector、HashTable、Collections 的 synchronizedCollection() 方法包裝的集合等。


          例如:如果一個線程刪除Vector 中的一個元素,另一個線程試圖獲取這個被刪除的元素,會拋出ArrayIndexOutOfBoundsException異常。


          實例代碼如下:

          public class VectorUnsafeDemo {
              private static Vector<Integer> vector = new Vector<>();

              public static void main(String[] args) {
                  while (true) {
                      for (int i = 0; i < 100; i++) {
                          vector.add(i);
                      }
                      ExecutorService executorService = Executors.newCachedThreadPool();
                      executorService.execute(() -> {
                          for (int i = 0; i < vector.size(); i++) {
                              vector.remove(i);
                          }
                      });
                      executorService.execute(() -> {
                          for (int i = 0; i < vector.size(); i++) {
                              vector.get(i);
                          }
                      });
                      executorService.shutdown();
                  }
              }
          }

          解決方式:為刪除和獲取進行同步(這里使用synchronized )


          代碼如下:

          executorService.execute(() -> {
              synchronized (vector) {
                  for (int i = 0; i < vector.size(); i++) {
                      vector.remove(i);
                  }
              }
          });
          executorService.execute(() -> {
              synchronized (vector) {
                  for (int i = 0; i < vector.size(); i++) {
                      vector.get(i);
                  }
              }
          });


          線程兼容

          線程兼容是指對象本身并不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在并發(fā)環(huán)境中可以安全地使用,我們平常說一個類不是線程安全的,絕大多數(shù)時候指的是這一種情況。Java API 中大部分的類都是屬于線程兼容的,如與前面的 Vector 和 HashTable 相對應的集合類 ArrayList 和 HashMap 等。


          線程對立

          線程對立是指無論調用端是否采取了同步措施,都無法在多線程環(huán)境中并發(fā)使用的代碼。由于 Java 語言天生就具備多線程特性,線程對立這種排斥多線程的代碼是很少出現(xiàn)的,而且通常都是有害的,應當盡量避免。


          實現(xiàn)線程安全的方式

          互斥同步(阻塞同步)

          互斥同步方式實現(xiàn)線程安全也是我們編程中最常用的實現(xiàn)方式。主要是使用 synchronized 和 ReentrantLock。

          如果想要初步了解 synchronized 和 ReentrantLock ,可以參考:


          聊一聊線程互斥與同步的那些事(以實例解釋synchronized與ReentrantLock)


          互斥同步方式是屬于阻塞方式,是一種悲觀的并發(fā)策略,性能上不如非阻塞同步方案。無論共享數(shù)據(jù)是否真的會出現(xiàn)競爭,它都要進行加鎖(這里討論的是概念模型,實際上虛擬機會優(yōu)化掉很大一部分不必要的加鎖)、用戶態(tài)核心態(tài)轉換、維護鎖計數(shù)器和檢查是否有被阻塞的線程需要喚醒等操作。


          悲觀策略:不做就不會做錯。(出錯的人總是那些干活的人,不干活的人是不會犯錯的)


          非阻塞同步

          非阻塞同步方案目前主流的有 CAS,Atomic類。


          CAS

          CAS是基于沖突檢測的樂觀并發(fā)策略 ,如果沒有其它線程爭用共享數(shù)據(jù),那操作就成功了,否則采取補償措施(不斷地重試,直到成功為止)。這種樂觀的并發(fā)策略的許多實現(xiàn)都不需要將線程阻塞,因此這種同步操作稱為非阻塞同步。樂觀鎖需要操作和沖突檢測這兩個步驟具備原子性,這里就不能再使用互斥同步來保證了,只能靠硬件來完成。硬件支持的原子性操作最典型的是: 比較并交換(Compare-and-Swap,CAS)。CAS 指令需要有 3 個操作數(shù),分別是內存地址 V、舊的預期值 A 和新值 B。當執(zhí)行操作時,只有當 V 的值等于 A,才將 V 的值更新為 B。


          硬件速度是高于軟件速度的,因此CAS是比同步互斥的方式性能更佳。


          Atomic類

          其實原子類的很多方法都是使用了Unsafe的CAS做非阻塞同步,因此在一定程度上說原子類只是CAS的一種JDK實現(xiàn),我們不需要關注內部實現(xiàn),直接使用即可,但是需要明白原子類實現(xiàn)線程安全的機制是非阻塞的,性能高于 synchronized 加鎖的對象。


          無同步方案

          上面說的兩種方式都是同步方案的兩種解決方案,而這種方案是 無同步。原理很簡單:


          如果一個方法本來就不涉及共享數(shù)據(jù),那它自然就無須任何同步措施去保證正確性


          實現(xiàn)無同步 的解決方案主要有 :棧封閉 ,線程本地存儲 和 可重入代碼。


          棧封閉

          首先,java使用棧封閉的方案有 :

          (1)JUC線程池: FutureTask詳解

          (2)JUC線程池: ThreadPoolExecutor詳解

          (3)JUC線程池:ScheduledThreadPool詳解

          (4)JUC線程池: Fork/Join框架詳解

          多個線程訪問同一個方法的局部變量時,不會出現(xiàn)線程安全問題,因為局部變量存儲在虛擬機棧中,屬于線程私有的。

          示例代碼如下:

          public class StackClosedExample {
              public void add100() {
                  int cnt = 0;
                  for (int i = 0; i < 100; i++) {
                      cnt++;
                  }
                  System.out.println(cnt);
              }
          }

          public static void main(String[] args) {
              StackClosedExample example = new StackClosedExample();
              ExecutorService executorService = Executors.newCachedThreadPool();
              executorService.execute(() -> example.add100());
              executorService.execute(() -> example.add100());
              executorService.shutdown();
          }

          結果如下:

          100
          100


          線程本地存儲(Thread Local Storage)

          本地存儲的java實現(xiàn) :Java 并發(fā) - ThreadLocal詳解


          如果一段代碼中所需要的數(shù)據(jù)必須與其他代碼共享,那就看看這些共享數(shù)據(jù)的代碼是否能保證在同一個線程中執(zhí)行。如果能保證,我們就可以把共享數(shù)據(jù)的可見范圍限制在同一個線程之內,這樣,無須同步也能保證線程之間不出現(xiàn)數(shù)據(jù)爭用的問題。


          符合這種特點的應用并不少見,大部分使用消費隊列的架構模式(如“生產者-消費者”模式)都會將產品的消費過程盡量在一個線程中消費完。其中最重要的一個應用實例就是經典 Web 交互模型中的“一個請求對應一個服務器線程”(Thread-per-Request)的處理方式,這種處理方式的廣泛應用使得很多 Web 服務端應用都可以使用線程本地存儲來解決線程安全問題。


          可以使用 java.lang.ThreadLocal 類來實現(xiàn)線程本地存儲功能。


          對于以下代碼,thread1 中設置 threadLocal 為 1,而 thread2 設置 threadLocal 為 2。過了一段時間之后,thread1 讀取 threadLocal 依然是 1,不受 thread2 的影響。


          示例代碼如下:

          public class ThreadLocalDemo {
              public static void main(String[] args) {
                  ThreadLocal threadLocal = new ThreadLocal();
                  Thread thread1 = new Thread(() -> {
                      threadLocal.set(1);
                      try {
                          Thread.sleep(1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println(threadLocal.get());
                      threadLocal.remove();
                  });
                  Thread thread2 = new Thread(() -> {
                      threadLocal.set(2);
                      threadLocal.remove();
                  });
                  thread1.start();
                  thread2.start();
              }
          }


          結果如下:

          1


          可重入代碼(Reentrant Code)

          這種代碼也叫做純代碼(Pure Code),可以在代碼執(zhí)行的任何時刻中斷它,轉而去執(zhí)行另外一段代碼(包括遞歸調用它本身),而在控制權返回后,原來的程序不會出現(xiàn)任何錯誤。


          可重入代碼有一些共同的特征,例如不依賴存儲在堆上的數(shù)據(jù)和公用的系統(tǒng)資源、用到的狀態(tài)量都由參數(shù)中傳入、不調用非可重入的方法等。


          彩蛋


          ————————————————

          版權聲明:本文為CSDN博主「keep-go-on」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉載請附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/qq_26462567/article/details/116142714





          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點贊支持下哈 

          瀏覽 23
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  人兽在线视频 | 99视频在线免费 | 色999国产在线视频 | 丁香花五月 | 亚洲视频在线视频播放 |