1. 程式人生 > 其它 >java 幾種同步方式

java 幾種同步方式

java的同步方式

在java主要有以下幾種方式

1.給變數/程式碼塊/方法/類新增悲觀鎖避免一個變數更改值的時候對這個變數進行更改。

用 sychronized 來進行同步
由於java的每個物件都有一個內建鎖,當用此關鍵字修飾方法時, 內建鎖會保護整個方法。在呼叫該方法前,需要獲得內建鎖,否則就處於阻塞狀態。
程式碼如:
public synchronized void save(){}
注: synchronized關鍵字也可以修飾靜態方法,此時如果呼叫該靜態方法,將會鎖住整個類
優缺點:
優點:加上synchronized鎖是為了保證伺服器選擇的順序性,使得併發操作的同一時刻有且僅有一個執行緒能夠讀取/寫入資料表記錄,防止多執行緒併發造成的髒資料。
缺點:悲觀鎖的引入,將會導致併發吞吐量發生明顯下降,付出相當大的效能代價。且不能獲得鎖是否是已經加上去了。

2.使用特殊域變數(volatile)實現執行緒同步

它的原理是每次要執行緒要訪問volatile修飾的變數時都是從記憶體中讀取,而不是存快取當中讀取,因此每個執行緒訪問到的變數值都是一樣的。這樣就保證了同步
a.volatile關鍵字為域變數的訪問提供了一種免鎖機制,
b.使用volatile修飾域相當於告訴虛擬機器該域可能會被其他執行緒更新,
c.因此每次使用該域就要重新計算,而不是使用暫存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final型別的變數
因為volatile使用了複合操作的原子性,所以下面的程式碼結果一定<=正確值


/**
 * 使用特殊域變數(volatile)實現執行緒同步
 *   a.volatile關鍵字為域變數的訪問提供了一種免鎖機制,
 *   b.使用volatile修飾域相當於告訴虛擬機器該域可能會被其他執行緒更新,
 *   c.因此每次使用該域就要重新計算,而不是使用暫存器中的值
 *   d.volatile不會提供任何原子操作,它也不能用來修飾final型別的變數
 */

public class VolatileKeywordSynchronization {

    class Bank {
        private volatile int accout = 10;


        public int getAccout() {
            return accout;
        }

        public void saveAccount(int money) {
            accout += money;
            System.out.println("account:" + accout);

        }

    }

    class VolatileThread implements Runnable {

        private Bank bank;

        public VolatileThread(Bank bank) {
            this.bank = bank;
        }

        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                bank.saveAccount(10);
                System.out.println(Thread.currentThread().getName() + "-->第" + i + "次當前賬戶餘額:" + bank.getAccout() + "元。");
            }
        }
    }


    public void userVolatileThread() {
        Bank bank = new Bank();

        VolatileThread volatileThread = new VolatileThread(bank);

        Thread thread1 = new Thread(volatileThread);

        Thread thread2 = new Thread(volatileThread);

        System.out.println("執行緒1:");
        thread1.start();

        System.out.println("執行緒2:");
        thread2.start();


    }

    public static void main(String[] args) {

        VolatileKeywordSynchronization volatileKeywordSynchronization = new VolatileKeywordSynchronization();

        volatileKeywordSynchronization.userVolatileThread();
    }


}

volatile的優缺點
優點:Volatile修飾的成員變數在每次被執行緒訪問是,都強迫從共享記憶體中重讀成員變數的值;當成員變數發生變化是,強迫執行緒將變化值回寫到共享記憶體;這樣可以讓多個執行緒總是看到某個成員變數的同一個值,Volatile關鍵字就是提示VM:對於這個成員變數不能儲存它的私有拷貝,而應該直接語共享成員變數互動;
缺點: 關鍵字能保證資料的可見性,但不能保證資料的原子性,且volatile只適用變數。
3.AtomicInteger(類)

public class VolatileKeywordSynchronization {

    class Bank {
        private AtomicInteger accout = new AtomicInteger(10);


        public int getAccout() {
            return accout.get();
        }

        public void saveAccount(int money) {
            accout.getAndAdd(money);
            System.out.println("account:" + accout);

        }

    }

    class VolatileThread implements Runnable {

        private Bank bank;

        public VolatileThread(Bank bank) {
            this.bank = bank;
        }

        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                bank.saveAccount(10);
                System.out.println(Thread.currentThread().getName() + "-->第" + i + "次當前賬戶餘額:" + bank.getAccout() + "元。");
            }
        }
    }


    public void userVolatileThread() {
        Bank bank = new Bank();

        VolatileThread volatileThread = new VolatileThread(bank);

        Thread thread1 = new Thread(volatileThread);

        Thread thread2 = new Thread(volatileThread);

        System.out.println("執行緒1:");
        thread1.start();

        System.out.println("執行緒2:");
        thread2.start();


    }

    public static void main(String[] args) {

        VolatileKeywordSynchronization volatileKeywordSynchronization = new VolatileKeywordSynchronization();

        volatileKeywordSynchronization.userVolatileThread();
    }
}

AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執行效率大為提升。 CAS 的原理是拿期望的值和原本的一個值作比較,如果相同則更新成新的值。UnSafe 類的 objectFieldOffset() 方法是一個本地方法,這個方法是用來拿到“原來的值”的記憶體地址,返回值是 valueOffset。另外 value 是一個 volatile 變數,在記憶體中可見,因此 JVM 可以保證任何時刻任何執行緒總能拿到該變數的最新值。
優缺點:
優點:不需要加鎖,就可以保證原子性,減少大量的開銷。
缺點:不能保證程式碼塊的原子性

ReentrantLock

ReentrantLock 意思為可重入鎖,指的是一個執行緒能夠對一個臨界資源重複加鎖。

public class VolatileKeywordSynchronization {
    ReentrantLock reentrantLock = new ReentrantLock();
    class Bank {
        private int accout = 10;

        public int getAccout() {
            return accout;
        }

        public void saveAccount(int money) {
            reentrantLock.lock();
            try {
                accout+=money;
            }finally {
                reentrantLock.unlock();
            }

            System.out.println("account:" + accout);

        }

    }

    class VolatileThread implements Runnable {

        private Bank bank;

        public VolatileThread(Bank bank) {
            this.bank = bank;
        }

        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bank.saveAccount(10);

                System.out.println(Thread.currentThread().getName() + "-->第" + i + "次當前賬戶餘額:" + bank.getAccout() + "元。");
            }
        }
    }


    public void userVolatileThread() {
        Bank bank = new Bank();

        VolatileThread volatileThread = new VolatileThread(bank);

        Thread thread1 = new Thread(volatileThread);

        Thread thread2 = new Thread(volatileThread);

        System.out.println("執行緒1:");
        thread1.start();

        System.out.println("執行緒2:");
        thread2.start();


    }

    public static void main(String[] args) {
        VolatileKeywordSynchronization volatileKeywordSynchronization = new VolatileKeywordSynchronization();

        volatileKeywordSynchronization.userVolatileThread();
    }
}