1. 程式人生 > >Java多執行緒中volatile的場景應用

Java多執行緒中volatile的場景應用

一、場景簡述

筆者在看多執行緒通訊相關問題時,不使用等待/通知機制實現多執行緒通訊的時候,發現b執行緒沒有與a執行緒發生正常通訊。

二、場景實現

如下是未發生正常通訊的程式碼

1、MyList類

package waitnotify;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: linjie
 * @description: 不使用等待/通知機制實現執行緒間的通訊 list類
 * @create: 2018/09/24 18:46
 */
public class MyList {
    private List list = new ArrayList();
    //向list中新增資料
    public void add(){
        list.add("xlj");
    }
    //返回list的大小
    public int size(){
        return list.size();
    }
}

2、執行緒類A

package waitnotify;

/**
 * @author: linjie
 * @description: 不使用等待/通知機制的執行緒類A
 * @create: 2018/09/24 18:50
 */
public class ThreadA extends Thread{
    private MyList list;
    //構造器
    public ThreadA(MyList list){
        super();
        System.out.println("a....");
        this.list = list;
    }
    @Override
    public void run(){
        try {
            for (int i = 0;i < 10;i++){
                list.add();
                System.out.println("添加了"+(i+1)+"個元素");
                Thread.sleep(1000);
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }

    }
}

3、執行緒類B

package waitnotify;

/**
 * @author: linjie
 * @description: 不使用等待/通知機制的執行緒類B
 * @create: 2018/09/24 19:12
 */
public class ThreadB extends Thread{
    private MyList list;
    public ThreadB(MyList list){
        super();
        System.out.println("b....");
        this.list = list;
    }
    @Override
    public void run(){
        try{
            while (true){
                if (list.size() == 5){
                    System.out.println("==5了,執行緒b要退出了!");
                    throw new InterruptedException();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

4、main類

package waitnotify;

/**
 * @author: linjie
 * @description:不使用等待/通知機制的main類
 * @create: 2018/09/24 19:20
 */
public class Test {
    public static void main(String[] args){
        MyList service = new MyList();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();
    }
}

執行結果如下

可以看到程式處於死迴圈,並且沒有出現 “==5了,執行緒b要退出了!”

三、場景分析

該現象與Java記憶體模型有關,Java記憶體模型如圖,可以看出每個執行緒都有一個自己的本地記憶體空間,執行緒執行時,先把變數從主記憶體讀取到執行緒自己的本地記憶體空間,然後再對該變數進行操作。然後對該變數操作完後,在某個時間再把該變數重新整理回主記憶體

所以場景程式碼中,A執行緒將list讀取到本地記憶體,修改後,再重新整理回主記憶體。

而在JVM 設定成 -server模式執行程式時,執行緒會一直在私有堆疊中讀取list大小,所以B執行緒一直無法讀取到A執行緒改變的list變數,從而處於死迴圈,並無法出現 “==5了,執行緒b要退出了!”

(圖片摘取地址在文末)

四、解決方案

線上程類B中對其變數前加volatile,作用即:它強制執行緒從主記憶體中取 volatile修飾的變數

volatile private MyList list;

加上之後執行結果正常

參考