volatile 可見性的模擬分析示例

  volatile 做爲java的關鍵字之一,必然有它存在的必要性;在不少的資料中,各位大神級的人物都對volatile作了深刻的分析,在這裏就不在贅述了;不清的朋友能夠遷移到這個地址詳細瞭解:https://www.cnblogs.com/dolphin0520/p/3920373.htmlhtml

  那麼已經瞭解volatile的做用。這裏呢?將使用java代碼將把volatile底層 「可見性」給擴大,並已代碼的形式展現,volatile的可見性究竟是怎麼一回事;java

  注意:本人亦不清楚volatile的底層是如何實現的,只是,僅僅只是經過各類資料中對volatile的分析,而後領悟出來的想法;(volatile 可能不是如此實現,切莫較真;有大神知道,也請爽快指教)緩存

  最後提示:此文僅供參考,切勿入坑;ide

  具體實現:測試

  1,首先模擬主內存,在該模擬的主內存中只存在一個地址,該地址用於存放一個共享變量;代碼以下;this

import io.netty.util.internal.ConcurrentSet;

public class MainMemory {

    //模擬主內存中的一塊內存地址,並存儲有一個數據 0 
    private int data = 0;
    
    //記錄持有該內存地址數據的全部對象,以便在內存地址數據被改變時,通知這些對象持有的數據無效;
    private ConcurrentSet<ICacheStatus> cacheHolder = new ConcurrentSet<>();

    /**
     * 模擬使用volatile關鍵字修飾的變量從主內存讀取數據:主內存將保持讀取者的一個狀態修改通知器,當主內存的數據被修改時,會第一時間通知到數據持有者;
     * read方法和write方式使用synchronized關鍵字修飾,是爲了模擬內存地址數據操做的原子性;
     */
    public synchronized int volatileRead(ICacheStatus cache) {
        cache.setStatus(true);
        cacheHolder.add(cache);
        return data;
    }
    
    /**
     * 模擬非volatile關鍵字修飾的變量從主內存中讀取數據
     */
    public synchronized int read() {
        return data;
    }
    
    /**
     * 模擬向內存地址中寫入數據
     */
    public synchronized void write(int outdata) {
        data = outdata;
        //通知緩存持有者,已持有的數據無效
        for(ICacheStatus holder : cacheHolder) {
            holder.setStatus(false);
        }
    }
    
    /**
     * 模擬緩存持有者釋放緩存,主內存將在之後的數據改變時,不通知改對象;
     * @param cacheHolder
     */
    public void releaseCache(ICacheStatus outcacheHolder) {
        cacheHolder.remove(outcacheHolder);
    }
    
}

  2, 緩存狀態通知器接口,代碼以下:spa

public interface ICacheStatus {

    void setStatus(boolean status);
}

  3,模擬線程本地緩存對象,該對象針對於變量是否被volatile變量修飾,提供兩種不一樣的操做:1,volatile修飾的變量,在使用時會檢查本地緩存的數據是否過時,如過時,則向主內存從新獲取; 2,非volatile修飾的變量,不檢查是否過時(固然,這不合理,sun也應該不是這麼實現的,僅供模擬   「volatile可見性」的演示;重要的事說n遍);代碼以下:public class ThreadLocalCache implements ICacheStatus     //本地緩存從主內存中讀取到的數    private int cache = -1;    /*.net

 * 模擬當前緩存數據的狀態,true可用 ,false爲不可用:須要向主內存中再次讀取數據; */ private boolean cacheFlag; //主內存 private MainMemory mainMemory; public ThreadLocalCache(MainMemory mm) { this.mainMemory = mm;
     this.cache = mm.volatileRead(this); } @Override
public void setStatus(boolean status) { this.cacheFlag = status; } //模擬變量被volatile關鍵字修飾,在使用前會檢查當期緩存的數據是否過時,若是過時則向主內存重新讀取; public int volatileRead() { if(!cacheFlag) cache = mainMemory.volatileRead(this); return cache; } //模擬非volatile變量的使用; public int read() {
return cache; } public void write(int data) { mainMemory.write(data); } /* * 模擬gc釋放資源 */ @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); mainMemory.releaseCache(this); } }

  4, 測試:模擬可見性的影響線程

  

    static public void main(String[] args) {
        
        //模擬一個主內存,而且在該主內存中存在一個共享變量
        final MainMemory mainMemory = new MainMemory();
        
        //1.0 模擬 《使用volatile關鍵字修飾的變量的方式讀寫數據時,volatile可見性的體現以及對共享變量的影響》
        //1.1模擬建立2個線程的 緩存
        ThreadLocalCache threadCache1 = new ThreadLocalCache(mainMemory);
        ThreadLocalCache threadCache2 = new ThreadLocalCache(mainMemory);
        
        //1.2 模擬兩個2線程同時讀取了一個volatile關鍵字修飾變量的值;
        int d1 = threadCache1.volatileRead();
        int d2 = threadCache2.volatileRead();
        System.out.println("獨立的線程緩存獲取到的數據:"  + " ;d1 = " + d1 + " ;d2 = " + d2);
        
        //1.3模擬線程1向主內存中寫入了數據,並打印主內存的值;
        threadCache1.write(threadCache1.volatileRead() + 1);
        System.out.println("當線程1修改主內存後,主內存中的值 = " + mainMemory.read());
        
        // 注意: 這裏將模擬volatile可見性,以及可見性對其它線程的影響;
        //1.4模擬線程2使用共享變量,並在該共享變量上加1;
        //在使用volatile修飾的共享變量時,會檢查當前線程緩存中的值是否可用,不然向 主內存中從新讀取;
        //這裏線程1在以前已修改了主內存的值,因此線程2值已被通知不可用,線程2向主內存從新讀取最新值;
        threadCache2.write(threadCache2.volatileRead() + 1);
        System.out.println("當線程1修改主內存後,主內存中的值 = " + mainMemory.read());
        
        
        //非volatile變量發生多個線程同時讀寫,修改值預期不一致演示
        
        //模擬一個主內存,而且在該主內存中存在一個共享變量
        final MainMemory mainMemory2 = new MainMemory();
        
        //2.0 模擬 《使用非volatile關鍵字修飾變量的方式讀寫數據時,對共享變量的影響》
        //1.1模擬建立2個線程的 緩存
        ThreadLocalCache threadCache3 = new ThreadLocalCache(mainMemory2);
        ThreadLocalCache threadCache4 = new ThreadLocalCache(mainMemory2);
        
        //1.2 模擬兩個2線程同時讀取了一個非volatile關鍵字修飾的共享變量的值;
        int d3 = threadCache3.read();
        int d4 = threadCache4.read();
        System.out.println("獨立的線程緩存獲取到的數據: "+ " ;d3 = " + d3 + " ;d4 = " + d4);
        
        //1.3模擬線程3向主內存中寫入了數據,並打印主內存的值;
        threadCache3.write(threadCache3.read() + 1);
        System.out.println("當線程3修改主內存後,主內存中的值 = " + mainMemory2.read());
        
        //注意 : 這裏將模擬使用非volatile變量,在線程緩存中的操做,由於當前緩存不知道數據已過時,並將已過時的數據
        //拿來使用,形成最後獲得的值,與預期值不一樣;
        //1.4模擬線程4向主內存中寫入了數據,並打印主內存的值;
        threadCache4.write(threadCache4.read() + 1);
        System.out.println("當線程4修改主內存後,主內存中的值 = " + mainMemory2.read());
        
    }
    
相關文章
相關標籤/搜索