什么是線程安全?
--當多個線程訪問某個類時,不管運行環境采用何種調度方式或者這些進程將如何交替執行,並且在主調代碼中不需要任何額外的協同或者同步,這個類都能表現出正確的行為,那么這個類是線程安全的。
1.原子性(Aumic包)
AutomicXXX類 :采用CAS機,即 unsafe.compare.AndSwapInt
public class AtomicControllerTest { public static int clientTotal = 5000; //請求總數 public static int threadTotal = 200; //同時並發的線程數 public static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); //創建線程池 final Semaphore semaphore = new Semaphore(threadTotal); //信號量(線程數) final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); //計數器 (把請求計數) for(int i =0 ; i < clientTotal ; i++){ executorService.execute(()->{ try { semaphore.acquire();//信號量 判斷進程是否執行 add(); semaphore.release(); } catch (Exception e){ log.error("Exception:", e); } countDownLatch.countDown(); }); } countDownLatch.await(); //當所有請求結束 executorService.shutdown(); log.info("count:{}", count); } private static void add(){ count.incrementAndGet(); } }
上面的代碼是線程安全的,要是將AutomicInteger換成Integer,add方法也隨着變成count++,線程將是不安全的。
incrementAndGet中的getAndAddInt是最為關鍵的代碼。
var1 是傳進來的對象(count), 如果執行3+1操作,var2是當前的值3,var 4 是1
var5 是底層的值,也就是從主內存拿到的值,由於其他線程也在進行操作,工作內存一直想主內存進行存取操作,主內存的值一直在變化
CAS操作的核心也就是保證從主內存拿到的值,是預期獲得的值並沒有被其他線程更改,所以在上面的方法中,一直在進行while操作,比較var2 和 var 5,
一旦相同,才會進行相加的操作。主內存和工作內存的運行圖如下:
synchronized:
1.修飾方法和修飾一個類時,效果一致,先獲得資源的線程,先運行,其余訪問改資源的線程需要等待
package com.justrun.threadthink.sync; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class SynchronizedExample2 { // 修飾一個類 public static void test1(int j) { synchronized (SynchronizedExample2.class) { for (int i = 0; i < 10; i++) { log.info("test1 {} - {}", j, i); } } } // 修飾一個靜態方法 public static synchronized void test2(int j) { for (int i = 0; i < 10; i++) { log.info("test2 {} - {}", j, i); } } public static void main(String[] args) { SynchronizedExample2 example1 = new SynchronizedExample2(); SynchronizedExample2 example2 = new SynchronizedExample2(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { example1.test2(1); }); executorService.execute(() -> { example2.test2(2); }); } }
11:42:20.273 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 0
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 1
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 2
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 3
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 4
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 5
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 6
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 7
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 8
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 9
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 0
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 1
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 2
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.syn
package com.justrun.threadthink.sync; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class SynchronizedExample1 { // 修飾一個代碼塊 public void test1(int j) { synchronized (this) { for (int i = 0; i < 10; i++) { log.info("test1 {} - {}", j, i); } } log.info("test-----", j); } // 修飾一個方法 public synchronized void test2(int j) { for (int i = 0; i < 10; i++) { log.info("test2 {} - {}", j, i); } } public static void main(String[] args) { SynchronizedExample1 example1 = new SynchronizedExample1(); SynchronizedExample1 example2 = new SynchronizedExample1(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { example1.test1(1); }); executorService.execute(() -> { example2.test1(2); }); } }
c.SynchronizedExample2 - test2 2 - 3
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 4
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 5
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 6
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 7
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 8
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 9
Process finished with exit code 0
2。syhcnhronized修飾一個代碼塊和修飾一個方法,其原理大致一致,如果代碼塊修飾的整個方法的內容,那它們是一致的
隨機調度資源,每個線程都有機會得到資源
package com.justrun.threadthink.sync; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class SynchronizedExample1 { // 修飾一個代碼塊 public void test1(int j) { synchronized (this) { for (int i = 0; i < 10; i++) { log.info("test1 {} - {}", j, i); } } log.info("test-----", j); } // 修飾一個方法 public synchronized void test2(int j) { for (int i = 0; i < 10; i++) { log.info("test2 {} - {}", j, i); } } public static void main(String[] args) { SynchronizedExample1 example1 = new SynchronizedExample1(); SynchronizedExample1 example2 = new SynchronizedExample1(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { example1.test1(1); }); executorService.execute(() -> { example2.test1(2); }); } }
11:52:20.619 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 0
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 1
11:52:20.619 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 0
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 2
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 1
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 3
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 2
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 4
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 3
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 5
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 6
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 7
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 8
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 9
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 4
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test-----
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 5
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 6
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 7
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 8
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 9
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test-----
volatile

volatile不適合存取操作,雖然能夠獲得主存中的最新的值,但無法保證更新后的值得唯一性