Java併發編程的藝術 記錄(二)

volatile的應用

  volatile的定義以下:Java編程語言容許線程訪問共享變量,爲了確保共享變量能被準確和一致地更新,線程應該確保經過排他鎖單獨得到這個變量。Java語言提供了volatile,在某些狀況下比鎖要更加方便。若是一個字段被聲明成volatile,Java線程內存模型確保全部線程看到這個變量的值是一致的。java

  緩存一致性協議(處理器):每一個處理器經過嗅探在總線上傳播的數據來檢查本身緩存的值是否是過時了,當處理器發現本身緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器對這個數據進行修改操做的時候,會從新從系統內存中把數據讀處處理器緩存裏。編程

  volatile的兩條實現原則 :緩存

    1)Lock前綴指令會引發處理器緩存回寫到內存 。安全

    2)一個處理器的緩存回寫到內存會致使其餘處理器的緩存無效 。編程語言

  LinkedTransferQueue 待看。ide

synchronized的實現原理與應用

  1.6及以後對synchronized作過優化。synchronized用的鎖是存在Java對象頭裏的。
優化

  synchronized實現同步的基礎:Java中的每個對象均可以做爲鎖 。atom

  synchronized鎖的4種狀態:級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級,意味着偏向鎖升級成輕量級鎖後不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是爲了提升得到鎖和釋放鎖的效率 。spa

  偏向鎖引入的目的:爲了讓線程得到鎖的代價更低而引入了偏向鎖 。線程

  偏向鎖的得到和撤銷流程:偏向鎖使用了一種等到競爭出現才釋放鎖的機制,因此當其餘線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖。

  輕量級鎖及膨脹流程圖

  鎖的優缺點

  處理器實現原子操做的兩種方式:

    1.總線鎖

    2.緩存鎖

  在Java中能夠經過鎖和循環CAS的方式來實現原子操做 。CAS(Compare and swap):CAS操做須要輸入兩個值,一個新值,一箇舊值,在操做期間先比較舊值有沒有發生變化,若無發生變化,則交換新值。

  自旋CAS:循環進行CAS操做直到成功爲止 。

示例:

package com.gjjun.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 線程安全的自增和線程不安全的自增
 * @author gjjun
 * @create 2018/8/12
 **/
public class CounterDemo {
    private AtomicInteger ai = new AtomicInteger(0);
    private int i = 0;
    public static void main(String[] args) {
        final CounterDemo cas = new CounterDemo();
        List<Thread> threads = new ArrayList<>(16);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        cas.count();
                        cas.safeCount();
                    }
                }
            });
            threads.add(t);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(cas.i);
        System.out.println(cas.ai.get());
        System.out.println(System.currentTimeMillis() - start);
    }

    /**
     * 不安全的計數
     */
    private void count() {
        i++;
    }

    /**
     * 使用cas實現線程安全計數器
     */
    private void safeCount() {
        for(;;) {
            int i = ai.get();

            boolean suc = ai.compareAndSet(i, ++i);

            if (suc) {

                break;
            }
        }
    }
}

  CAS的三個問題:

    1.ABA問題。一個值由A到B再到A,CAS則認爲是無變化的,但其實是變化的。使用版本號解決:1A-2B-3A,類爲AtomicStampedReference。

    2.循環時間長開銷大。可使用處理器的pause指令,來略微提高效率。

    3.只能保證一個共享變量的原子操做。可使用鎖,或者使用AtomicReference類保證引用對象之間的原子性。

相關文章
相關標籤/搜索