詳解Java synchronized關鍵字的用法
當你在洗手間時,門是被鎖定的,這意味著沒有其他人可以走進來并干擾你。同樣,在多線程編程中也存在這樣的問題,如果多個線程同時訪問同一塊共享內存,那么就會產生競態(tài)條件,可能導致數據丟失或不一致的情況。為了避免這種情況,在多線程編程中使用鎖機制來確保同一時刻只有一個線程能夠修改共享內存。Java 中使用 synchronized 作為鎖機制,讓我們來學習一下如何使用 synchronized 實現(xiàn)線程安全。
1. synchronized 的基本概念
在 Java 中,synchronized 可以用來鎖定一個對象,從而達到保護多個線程訪問共享數據的目的。當一個線程獲取了 synchronized 鎖后,在未釋放鎖之前,其他線程不能獲取該鎖。相應地,這個線程也不能獲取其他線程已經獲取的鎖。
2. synchronized 的兩種使用方式
synchronized 關鍵字可以用在方法級別和代碼塊級別,下面分別介紹兩種使用方式。
2.1 方法級別
我們可以將 synchronized 用在方法級別上,這種情況下鎖定的對象是當前對象(this)。
public class MyClass { public synchronized void myMethod() { // synchronized 代碼 } }
2.2 代碼塊級別
我們也可以將 synchronized 關鍵字用在代碼塊級別上,這種情況下鎖定的對象可以是當前對象(this),也可以是任意一個對象。
public class MyClass { private final Object lock = new Object(); // 定義一個對象作為鎖 public void myMethod() { synchronized (lock) { // synchronized 代碼 } } }
這種情況下,我們可以在任何時間使用 lock 對象來同步操作,當然,也可以使用其他對象作為鎖。
3. synchronized 的示例
示例1
在下面的示例中,我們定義了一個計數器 Counter,在 Counter 的 run() 方法中使用 synchronized 關鍵字來保證多個線程對 count 變量的訪問安全。我們創(chuàng)建了 10 個線程,并且每個線程對計數器進行 1000 次增操作,最后輸出計數器的值。
public class Test { public static void main(String[] args) { Counter counter = new Counter(); Thread[] threads = new Thread[10]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(counter); threads[i].start(); } for (int i = 0; i < threads.length; i++) { try { threads[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Final count: " + counter.getCount()); } static class Counter implements Runnable { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } @Override public void run() { for (int i = 0; i < 1000; i++) { increment(); } } } }
在這個例子中,我們創(chuàng)建了 10 個線程,每個線程對計數器進行了 1000 次增加操作,這種并發(fā)場景下如果沒有加鎖機制,將會導致數據不一致。但是通過使用 synchronized 關鍵字的加鎖機制,我們保證了計數器的安全訪問,并且最終輸出的計數器的值也是正確的。
示例2
模擬在取款過程中可能出現(xiàn)的問題:
class BankAccount { private int balance; public BankAccount(int initialBalance) { this.balance = initialBalance; } public synchronized void withdraw(int amount) { System.out.println(Thread.currentThread().getName() + " 開始取款"); try { // 模擬取款過程中的延遲 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (amount <= balance) { balance -= amount; System.out.println(Thread.currentThread().getName() + " 成功取款: " + amount); } else { System.out.println(Thread.currentThread().getName() + " 余額不足"); } System.out.println(Thread.currentThread().getName() + " 取款后余額為: " + balance); } } public class BankWithdrawalExample { public static void main(String[] args) { BankAccount account = new BankAccount(1000); Thread thread1 = new Thread(() -> account.withdraw(500), "Thread 1"); Thread thread2 = new Thread(() -> account.withdraw(700), "Thread 2"); thread1.start(); thread2.start(); } }
在以上示例中,withdraw方法模擬了取款操作,并在其中加入了1秒的延遲。
withdraw方法加上和不加synchronized的對比結果:
不加synchronized的結果:
Thread 1 開始取款
Thread 2 開始取款
Thread 2 成功取款: 700
Thread 2 取款后余額為: 300
Thread 1 成功取款: 500
Thread 1 取款后余額為: -200
可以看到,由于沒有對銀行賬戶的取款方法進行同步控制,兩個線程同時進入了取款方法,導致賬戶余額計算錯誤,出現(xiàn)了負數的情況。
加上synchronized的結果:
Thread 1 開始取款
Thread 1 成功取款: 500
Thread 1 取款后余額為: 500
Thread 2 開始取款
Thread 2 余額不足
Thread 2 取款后余額為: 500
可以看到,加上synchronized之后,兩個線程依次進入了取款方法,避免了資源競爭的問題,從而保證了賬戶余額的正確性。
4. synchronized 的注意事項
使用 synchronized 時,需要注意以下幾點:
- synchronized 關鍵字只能用于方法和代碼塊內部,不能用于類和接口。
- synchronized 鎖定的對象是當前對象(this)或指定的對象,要注意鎖對象不應該是一個字符串或者數字等常量,因為這樣可能導致死鎖情況。
- synchronized 的開銷很大,每次加鎖和釋放鎖都需要進行系統(tǒng)調用,需要注意性能問題。
- synchronized 僅能解決單 JVM 內的線程同步問題,對于多線程分布式環(huán)境,需要考慮分布式鎖的解決方案。
5. 總結
synchronized 關鍵字是 Java 中用來確保線程安全的基本機制。通過使用 synchronized,我們可以鎖定一個對象,從而確保同一時刻只有一個線程可以訪問該對象。可以將 synchronized 用于方法級別和代碼塊級別,要注意鎖定的對象應該是一個合適的對象,不能是一個常量。同時,需要注意性能問題和分布式環(huán)境下的線程同步問題。
到此這篇關于詳解Java synchronized關鍵字的用法的文章就介紹到這了,更多相關Java synchronized關鍵字內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
VSCode+Gradle搭建Java開發(fā)環(huán)境實現(xiàn)
這篇文章主要介紹了VSCode+Gradle搭建Java開發(fā)環(huán)境實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07使用@ConditionalOnProperty控制是否加載的操作
這篇文章主要介紹了使用@ConditionalOnProperty控制是否加載的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08