Java線程同步的四種方式詳解
Java線程同步屬于Java多線程與并發(fā)編程的核心點(diǎn),需要重點(diǎn)掌握,下面我就來詳解Java線程同步的4種主要的實(shí)現(xiàn)方式
什么是Java線程同步
當(dāng)使用多個線程來訪問同一個數(shù)據(jù)時,將會導(dǎo)致數(shù)據(jù)不準(zhǔn)確,相互之間產(chǎn)生沖突,非常容易出現(xiàn)線程安全問題,如下圖所示:
比如多個線程都在操作同一數(shù)據(jù),都打算修改商品庫存,這樣就會導(dǎo)致數(shù)據(jù)不一致的問題。
線程同步的真實(shí)意思,其實(shí)是“排隊”:幾個線程之間要排隊,一個一個對共享資源進(jìn)行操作,而不是同時進(jìn)行操作。
所以我們用同步機(jī)制來解決這些問題,加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調(diào)用,從而保證了該變量的唯一性和準(zhǔn)確性。
Java線程同步的幾種方式
1、使用synchronized關(guān)鍵字
這種方式比較靈活,修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊。
其作用的范圍是大括號{}括起來的代碼,作用的對象是調(diào)用這個代碼塊的對象,如下格式:
synchronized(對象) { //得到對象的鎖,才能操作同步代碼 需要被同步代碼; }
通常沒有必要同步整個方法,使用synchronized代碼塊同步關(guān)鍵代碼即可。
具體的示例如下:
public class SynchronizedThread { class Bank { private int account = 200; public int getAccount() { return account; } /** * 用同步方法實(shí)現(xiàn) * * @param money */ public synchronized void save(int money) { account += money; } /** * 用同步代碼塊實(shí)現(xiàn) * * @param money */ public void save1(int money) { synchronized (this) { account += money; } } } class NewThread implements Runnable { private Bank bank; public NewThread(Bank bank) { this.bank = bank; } @Override public void run() { for (int i = 0; i < 10; i++) { // bank.save1(10); bank.save(10); System.out.println(i + "賬戶余額為:" + bank.getAccount()); } } } /** * 建立線程,調(diào)用內(nèi)部類 */ public void useThread() { Bank bank = new Bank(); NewThread new_thread = new NewThread(bank); System.out.println("線程1"); Thread thread1 = new Thread(new_thread); thread1.start(); System.out.println("線程2"); Thread thread2 = new Thread(new_thread); thread2.start(); } public static void main(String[] args) { SynchronizedThread st = new SynchronizedThread(); st.useThread(); } }
2.使用ReentrantLock
ReentrantLock類是可重入、互斥、實(shí)現(xiàn)了Lock接口的鎖,它與使用synchronized方法具有相同的基本行為和語義,并且擴(kuò)展了其能力。
private int account = 100; //需要聲明這個鎖 private Lock lock = new ReentrantLock(); public int getAccount() { return account; } //這里不再需要synchronized public void save(int money) { lock.lock(); try{ account += money; }finally{ lock.unlock(); } }
synchronized 與 Lock 的對比
ReentrantLock是顯示鎖,手動開啟和關(guān)閉鎖,別忘記關(guān)閉鎖;
synchronized 是隱式鎖,出了作用域自動釋放;
ReentrantLock只有代碼塊鎖,synchronized 有代碼塊鎖和方法鎖;
使用 ReentrantLock鎖,JVM 將花費(fèi)較少的時間來調(diào)度線程,線程更好,并且具有更好的擴(kuò)展性(提供更多的子類);
優(yōu)先使用順序:
ReentrantLock> synchronized 同步代碼塊> synchronized 同步方法
3.使用原子變量實(shí)現(xiàn)線程同步
為了完成線程同步,我們將使用原子變量(Atomic***開頭的)來實(shí)現(xiàn)。
比如典型代表:AtomicInteger類存在于java.util.concurrent.atomic中,該類表示支持原子操作的整數(shù),采用getAndIncrement方法以原子方法將當(dāng)前的值遞加。
具體示例如下:
private AtomicInteger account = new AtomicInteger(100); public AtomicInteger getAccount() { return account; } public void save(int money) { account.addAndGet(money); }
4.ThreadLocal實(shí)現(xiàn)線程同步
如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本,副本之間相互獨(dú)立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響,從而實(shí)現(xiàn)線程同步。
具體代碼示例如下:
//只改Bank類,其余代碼與上同 public class Bank{ // 創(chuàng)建一個線程本地變量 ThreadLocal private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){ @Override //返回當(dāng)前線程的"初始值" protected Integer initialValue(){ return 100; } }; public void save(int money){ //設(shè)置線程副本中的值 account.set(account.get()+money); } public int getAccount(){ //返回線程副本中的值 return account.get(); } }
以上就是Java線程同步的四種方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Java線程同步的四種方式詳解的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot中如何通過yml為實(shí)體類注入屬性
這篇文章主要介紹了Springboot中如何通過yml為實(shí)體類注入屬性,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05MyBatisPlus自定義SQL的實(shí)現(xiàn)
MyBatisPlus提供了自定義SQL功能,允許開發(fā)者在Mapper接口中定義方法,并通過XML文件或注解編寫SQL語句,本文詳解了如何在MP中使用自定義SQL,感興趣的可以了解一下2024-09-09JavaEE中關(guān)于ServletConfig的小結(jié)
ServletConfig是針對特定的Servlet的參數(shù)或?qū)傩?。ServletConfig是表示單獨(dú)的Servlet的配置和參數(shù),只是適用于特定的Servlet。從一個servlet被實(shí)例化后,對任何客戶端在任何時候訪問有效,但僅對本servlet有效,一個servlet的ServletConfig對象不能被另一個servlet訪問2014-10-10Spring中一個少見的引介增強(qiáng)IntroductionAdvisor
這篇文章主要為大家介紹了Spring中一個少見的引介增強(qiáng)IntroductionAdvisor實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08