天天看點

Java線程同步的四種方式詳解(建議收藏)

作者:mikechen的網際網路架構
Java線程同步的四種方式詳解(建議收藏)

Java線程同步屬于Java多線程與并發程式設計的核心點,需要重點掌握,下面我就來詳解Java線程同步的4種主要的實作方式@mikechen

什麼是線程同步

當使用多個線程來通路同一個資料時,将會導緻資料不準确,互相之間産生沖突,非常容易出現線程安全問題,如下圖所示:

Java線程同步的四種方式詳解(建議收藏)

比如多個線程都在操作同一資料,都打算修改商品庫存,這樣就會導緻資料不一緻的問題。

線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。

是以我們用同步機制來解決這些問題,加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調用,進而保證了該變量的唯一性和準确性。

線程同步的幾種方式

Java線程同步的四種方式詳解(建議收藏)

1、使用synchronized關鍵字

這種方式比較靈活,修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊。

其作用的範圍是大括号{}括起來的代碼,作用的對象是調用這個代碼塊的對象,如下格式:

synchronized(對象) {				
  //得到對象的鎖,才能操作同步代碼    需要被同步代碼;
}            

通常沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼即可。

具體的示例如下:

public class SynchronizedThread {
 
    class Bank {
 
        private int account = 200;
 
        public int getAccount() {
            return account;
        }
 
        /**
         * 用同步方法實作
         *
         * @param money
         */
        public synchronized void save(int money) {
            account += money;
        }
 
        /**
         * 用同步代碼塊實作
         *
         * @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());
            }
        }
 
    }
 
    /**
     * 建立線程,調用内部類
     */
    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();
    }
 
}           

如果你還想深入了解Synchronized的底層原理,可以看 Synchronized的實作原理詳解(看這篇就夠了)

2.使用ReentrantLock

ReentrantLock類是可重入、互斥、實作了Lock接口的鎖,它與使用synchronized方法具有相同的基本行為和語義,并且擴充了其能力。

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是顯示鎖,手動開啟和關閉鎖,别忘記關閉鎖;

synchronized 是隐式鎖,出了作用域自動釋放;

ReentrantLock隻有代碼塊鎖,synchronized 有代碼塊鎖和方法鎖;

使用 ReentrantLock鎖,JVM 将花費較少的時間來排程線程,線程更好,并且具有更好的擴充性(提供更多的子類);

優先使用順序:

ReentrantLock> synchronized 同步代碼塊> synchronized 同步方法

3.使用原子變量實作線程同步

為了完成線程同步,我們将使用原子變量(Atomic***開頭的)來實作。

比如典型代表:AtomicInteger類存在于java.util.concurrent.atomic中,該類表示支援原子操作的整數,采用getAndIncrement方法以原子方法将目前的值遞加。

具體示例如下:

private AtomicInteger account = new AtomicInteger(100);
 
        public AtomicInteger getAccount() {
            return account;
        }
 
        public void save(int money) {
            account.addAndGet(money);
        }           

4.ThreadLocal實作線程同步

如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本,副本之間互相獨立,這樣每一個線程都可以随意修改自己的變量副本,而不會對其他線程産生影響,進而實作線程同步。

具體代碼示例如下:

//隻改Bank類,其餘代碼與上同
        public class Bank{
            // 建立一個線程本地變量 ThreadLocal
            private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
                @Override
                //傳回目前線程的"初始值" 
                protected Integer initialValue(){
                    return 100;
                }
            };
            public void save(int money){
                //設定線程副本中的值
                account.set(account.get()+money);
            }
            public int getAccount(){
                //傳回線程副本中的值 
                return account.get();
            }
        }           

以上

98.5%看過架構技術合集

  • 分布式架構設計從0到1全部合集(附:分布式、微服務、高并發等大型網站架構)
  • JVM(Java虛拟機)從0到1全部合集(建議收藏)
  • Java多線程與并發從0到1全部合集(面試必看)
  • Redis分布式緩存從0到1全部合集(進階必看)
  • Spring開發架構從0到1全部合集(建議收藏)
  • MySQL資料庫從0到1全部合集(建議收藏)
Java線程同步的四種方式詳解(建議收藏)

繼續閱讀