1.JVM對sychronized的優化:鎖膨脹、鎖消除、鎖粗化、自適應自旋鎖
(1)鎖膨脹:從無鎖、偏向鎖、輕量級鎖、重量級鎖的過程叫做鎖膨脹。在JDK1.6以前,sychronized是由重量級鎖實現的,加鎖和解鎖的過程需要從用戶態切換到內核態,性能低下。有了鎖膨脹機制,大多數場景可以使用無鎖、偏向鎖和輕量級鎖,在執行并發操作的時候,避免了線程從用戶態到內核態,從而減少性能的開銷。
(2)鎖消除:JVM檢測到代碼段不再被共享和競爭,此時就會取消該代碼段的所屬的代碼段。
(3)鎖粗化:將多個連續的加鎖、解鎖操作鏈接在一起,形成一個范圍更大的鎖。
(4)自適應自旋鎖:通過自身循環,獲取鎖的一種方式,避免了線程開啟和線程掛起的性能開銷。因為線程開啟和線程掛起需要從用戶態轉到內核態,這個過程是比較緩慢的,性能比較低下。
2.介紹AQS
3.CAS和AQS的關系
4.用AQS實現可重入的公平鎖
5.ThreadLocal的作用
6.樂觀鎖和悲觀鎖
7.java中實現樂觀鎖的方式
8.CAS的缺點
9.為什么不能所有的鎖都用CAS
10.CAS有什么問題,java是如何解決的
11.volatile的作用
volatile可以保證可見性,不能保證原子性,所以會引發線程安全的問題。如果一個線程修改了使用volatile關鍵字的修飾的變量,其他線程也能獲取到這個變量的最新值,從而避免了數據不一致的狀態。
對于復合操作,比如i++這種自增操作,因為不是原子操作,如果有多個線程修改了i的值,volatile是不能保證線程安全的。需要用Sychronized和Lock來保證原子性和線程安全。
12.volatile和sychronized的比較
13.什么是公平鎖和非公平鎖。
14.非公平鎖的吞吐量為什么比公平鎖大。
15.reentrantlock是怎么實現公平鎖的。
公平鎖和非公平鎖的區別是,公平鎖中有hasQueueProcessors()=false方法,來看自己之前還有線程在排隊嗎
hasQueuedPredecessors() 用來判斷 當前等待隊列是否有線程在排隊獲取鎖。對于非公平鎖,無論是否已經有線程在排隊,都會嘗試獲取鎖,獲取不到再排隊。
tryLock()方法是非公平的,可以插隊
16.線程池的核心參數
new SynchronousQueue<>() 是 Java 并發包 里的一個用法。SynchronousQueue 是 java.util.concurrent 提供的一種特殊的 阻塞隊列。
和普通的隊列(ArrayBlockingQueue、LinkedBlockingQueue)不同:它不存儲任何元素,容量是 0。
每次 put() 必須等到有另一個線程 take(),才能成功;反過來 take() 也必須等到有人 put()。
所以它其實是一個 線程之間直接移交(handoff)數據的工具。
把 SynchronousQueue 想象成 過手交易:
線程 A 想交給線程 B 一個包裹。
A 必須等到 B 伸手來拿(調用 take()),才能把東西放出去。
它不像普通隊列能“先存進去,等別人慢慢取”。
17.await()方法
(1)Condition.await()
在 java.util.concurrent.locks.Condition 接口中,await() 是用來 讓當前線程等待,直到其他線程通過 signal() 或 signalAll() 喚醒它。常用場景是自定義鎖下的線程等待/通知機制。
import java.util.concurrent.locks.*;public class AwaitExample {private static final Lock lock = new ReentrantLock();private static final Condition condition = lock.newCondition();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {lock.lock();try {System.out.println("Thread t1: Waiting...");condition.await(); // 等待被喚醒System.out.println("Thread t1: Woke up!");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}});Thread t2 = new Thread(() -> {lock.lock();try {System.out.println("Thread t2: Sleeping for 2s...");Thread.sleep(2000);condition.signal(); // 喚醒等待的線程System.out.println("Thread t2: Sent signal!");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}});t1.start();t2.start();t1.join();t2.join();}
}
await() 會釋放鎖并讓當前線程等待。
signal() 或 signalAll() 會喚醒等待線程。
與 Object.wait() 類似,但更靈活,可以在 Lock 中使用。
(2)CountDownLatch.await()
在 java.util.concurrent.CountDownLatch 中,await() 是 阻塞當前線程,直到計數器為 0。
CountDownLatch latch = new CountDownLatch(3);for (int i = 0; i < 3; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " finished work");latch.countDown(); // 計數器減 1}).start();
}latch.await(); // 阻塞,直到計數器為 0
System.out.println("All threads finished!");
(3)CompletableFuture.await()(類似概念) 不常用
在 異步編程里,比如在某些庫或 Kotlin/JavaScript 的 async/await 中,await 用來 等待異步操作完成并獲取結果。Java 標準庫里 CompletableFuture 沒有 await() 方法,但可以用 get() 或 join() 實現類似效果。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
String result = future.join(); // 等待結果
System.out.println(result); // 輸出 "Hello"
18.例子3
join() → 一直等,直到目標線程結束。
前面例子里寫的 t1.join(); t2.join(); 的意思就是:
主線程會先等待 t1 執行結束,再等待 t2 執行結束,最后才繼續往下走。這樣可以確保兩個子線程都跑完。
假設兩個線程并發讀寫同一個整型變量,初始值為零,每個線程加 50次,結果可能是什么?
在沒有任何同步機制的情況下,兩個線程并發對同一個整型變量進行 50 次加1操作,最終結果可能是100,也可能小于 100,最壞的結果是 50,也就是最終的結果可能是在 [50,100]。
小于 100 情況的分析,由于對整型變量的 num++ 操作不是原子操作,它實際上包含了三個步驟:讀取變量的值、將值加 1、將新值寫回變量。在多線程環境下,可能會出現線程安全問題。例如,線程1和線程2同時讀取了變量的當前值,然后各自將其加 1,最后都將相同的新值寫回變量,這就導致了一次加 1操作的丟失。這種情況會多次發生,最終結果就會小于 100。
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerAddition {private static AtomicInteger num = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException{Thread thread1 = new Thread(()->{for(int i=0;i<50;i++){num.incrementAndGet();}});Thread thread2 = new Thread(()-> {for(int i=0;i<50;i++){num.incrementAndGet();}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("最終結果:"+ num.get());}}
通過sychronized方法,保證操作的互斥性。
public class SynchronizedAddition {private static int num =0;private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(()-> {for(int i=0;i<50;i++){synchronized(lock){num++;}}});Thread thread2 = new Thread(()-> {for (int i = 0; i < 50; i++) {synchronized (lock) {num++;}}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("最終結果:"+ num);}}