1. 程式人生 > >JAVA 執行緒之記憶體可見性

JAVA 執行緒之記憶體可見性

什麼是記憶體可見性

先介紹幾個概念:

共享變數

如果一個變數在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是這幾個執行緒的共享變數

可見性

一個執行緒對共享變數值的修改,能夠及時地被其他執行緒看到

JMM( JAVA記憶體模型)

JAVA記憶體模型(Java Memory Model) 描述了Java程式中各種變數(共享變數)的訪問規則,以及在JVM中將變數儲存到記憶體中和從記憶體中讀取出變數這樣的底層細節

這裡我們只需要知道:

  1. 所有變數都儲存在主記憶體中
  2. 每個執行緒都有自己獨立的工作記憶體,裡面儲存該執行緒使用到的變數副本(主記憶體中該變數的一份拷貝)

這裡寫圖片描述

它有兩條規定:

  1. 執行緒對共享變數的所有操作必須在自己的工作記憶體中進行,不可直接從主記憶體中讀取
  2. 不同執行緒間無法直接訪問其他執行緒工作區間中的變數,需要通過主記憶體來完成

因此要想實現執行緒1對共享變數的修改被執行緒2及時看到,要經過兩個步驟:

  1. 把工作記憶體1中更新過的共享變數重新整理到主記憶體

這裡寫圖片描述

2.把主記憶體中更新過的共享變數重新整理到工作記憶體2

這裡寫圖片描述

Synchronized實現可見性

先了解下重排序和as-if-serial語句的概念

重排序

程式碼書寫的順序和實際執行的順序不同

程式碼順序

    int number = 1;
    int
result = 0;

執行順序

    int result = 0;
    int number = 1;

原因是編譯器或者處理器為了提高程式效能做的優化

  1. 編譯器優化的重排序 (編譯器優化)
  2. 指令級並行重排序(處理器優化)
  3. 記憶體系統的重排序(處理器優化)

as-if-serial

無論如何重排序,程式執行的結果應該和程式碼執行的結果一致(JAVA編譯器、執行時和處理器都會保證JAVA在單執行緒下遵循as-if-serial)

    int num1 = 1;
    int num2 = 2;
    int sum = num1 + num2;

單執行緒:第1,2行順序可以重排 但是第3行不行

重排序不會給單執行緒帶來記憶體可見性問題

多執行緒中程式交錯執行時可能會造成記憶體可見性問題

Synchronized可以實現:

  • 原子性 (同步)
  • 可見性

可見性

JMM關於Synchronized的兩條規定:

  1. 執行緒解鎖前,必須把共享變數的最新值重新整理到主記憶體中
  2. 執行緒加鎖時,將清空工作記憶體中的共享變數的值,從而使用共享變數時都必須從主記憶體中重新讀取最新的值

因此執行緒解鎖前對共享變數的修改在下次加鎖時對其他執行緒是可見的

執行緒執行互斥程式碼的過程:

  1. 獲得互斥鎖
  2. 清空工作記憶體
  3. 從主記憶體拷貝變數的最新副本到工作記憶體
  4. 執行程式碼
  5. 將更改後的共享變數的值重新整理到主記憶體
  6. 釋放互斥鎖

程式碼示例:

package thread.sychronized;

/**
 * SyncDemo
 * Created by heqianqian on 2017/4/15.
 */
public class SyncDemo {

    private boolean ready = false;
    private int value = 2;
    private int result = 0;

    private synchronized void read() {
        if(ready){
            result = value*3;
        }
        System.out.println("result的值為:" + result);
    }

    private synchronized void write() {
        ready = true;
        value = 10;
    }

    private class MyThread extends Thread{

        private boolean flag;

        public MyThread(boolean flag) {
            this.flag = flag;
        }

        @Override
        public void run() {
            if (flag){
                write();
            }else{
                read();
            }
        }
    }

    public static void main(String[] args) {
        SyncDemo syncDemo = new SyncDemo();
        //寫操作
        syncDemo.new MyThread(true).start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //讀操作
        syncDemo.new MyThread(false).start();
    }

}

Volatile實現可見性

通過加入記憶體屏障和禁止重排序優化來實現

  • 對volatile變數進行寫操作時 會在寫操作後加入一條store屏障指令
  • 對volatile變數進行寫操作時 會在讀操作前加入一條load屏障指令

通俗的來說:

volatile變數在每次被執行緒訪問時,都強迫從主記憶體中讀取該變數的值,而當該變數發生改變時,又會強迫把最近的值重新整理到主記憶體中

volatile不能保證符合操作的原子性

保證操作的原子性

  1. 使用synchronized
  2. 用ReentranLock實現

要在多執行緒中安全的使用volatile變數 必須同時滿足

  1. 對變數的寫入操作不依賴當前值
  2. 變數沒有包含在具有其他變數的不變式中

相關推薦

JAVA 執行記憶體可見

什麼是記憶體可見性 先介紹幾個概念: 共享變數: 如果一個變數在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是這幾個執行緒的共享變數 可見性: 一個執行緒對共享變數值的修改,能夠及時地被其他執行緒看到 JMM( JAVA記憶體模型)

細說Java執行記憶體可見

前言: 討論學習Java中的記憶體可見性、Java記憶體模型、指令重排序、as-if-serial語義等多執行緒中偏向底層的一些知識,以及synchronized和volatile實現記憶體可見性的原理和方法。 1、可見性介紹 可見性:一個執行緒對共用變數值的修改,能夠及時地被其他執行緒

Java 中的多執行記憶體可見

1. Java記憶體模型(JMM) Java 記憶體模型(Java Memory Model)描述了Java程式中各種變數(執行緒共享變數)的訪問規則,以及在 JVM 中將變數儲存到記憶體和從記憶體中讀取出變數的底層細節。 所有的變數都儲存在

Java執行記憶體可見和原子:Synchronized和Volatile的比較

在刷題時,碰到一題:關於volatile關鍵字的說法錯誤的是: A. 能保證執行緒安全 B volatile關鍵字用在多執行緒同步中,可保證讀取的可見性  C JVM保證從主記憶體載入到執行緒工做記憶體的值是最新的 D volatile能禁止指令進行指令重排序 答案:A 處

Java執行記憶體可見實現方式

可見性的實現方式 Java語言層面支援的可見性實現方式: - synchronized - volatile synchronized實現可見性原理 synchronized可以實現: 原子性(同步) 可見性 JMM關於synchronized

java執行記憶體可見

可見性:一個執行緒對共享變數值的修改,能夠及時地被其他執行緒看到。 共享變數:如果一個變數在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是幾個執行緒的共享變數。  e.g:主記憶體當中有一個變數x,多個執行緒同時包含有一個x的副本,當某個執行緒的工作的x值改變的時候要及時地被執行緒看到。就會一個問題,

詳解Java執行記憶體可見

可見性:一個執行緒對共享變數值的修改,能夠及時的被其他執行緒看到。  共享變數:如果一個電量在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是這幾個執行緒的共享變數。 JAVA記憶體模型(Java Memory Model)描述了Java程式中各種變數(執行緒共享變

JAVA】多執行記憶體可見

                                    多執行緒之記憶體可見性 一、什麼是可見性? 一個執行緒對共享變數值的修改,能夠及時地被其他執行緒所看到。 共享變數:如果一個變數在多個執行緒的工作記憶體中都存在副本,那麼這個變數就是這幾個執行緒的共

執行記憶體可見Volatile(一)

從這篇博文開始,我們開始分享一些多執行緒的內容,畢竟在工作中,使用多執行緒比較多。多總結一下,終歸沒有壞處。這個系列的文章不會特別長,爭取在3到5分鐘之間結束,主要以說明白內容,給出相應的解決方案,重點在於實踐。 如標題所示,這篇博文我們簡單的介紹一下記憶體可

5.多執行記憶體可見

共享變數線上程間的可見性 共享變數:如果一個變數在多個執行緒的工作記憶體中都存在副本, 那麼這個變數就是這幾個執行緒的共享變數 可見性:一個執行緒對共享變數值的修改,能夠及時的被其他執行緒看到 Java記憶體模型(JM

Java執行記憶體可見

Java中對於volatile變數,通俗點說可以把它看做多執行緒之間分享的共享記憶體,可見性是立即的。 實際上它分成了兩部分,volatile write和volatile read。由於Unsafe提供了getXXXVolatile和putXXXVolatile介面。所以

Volatile關鍵字以及執行記憶體可見問題

一、Volatile關鍵字 作用: 當多個執行緒進行操作共享資料時,可以保證記憶體中的資料可見,即為一個執行緒對資料的修改對另外一個執行緒來說是可見的。相較於 synchronized 是一種較為輕量級的同步策略。但不保證互斥可原子性。 二、簡單使用 package com.duchong.ju

Java記憶體模型以及執行安全的可見問題

Java記憶體模型 VS JVM執行時資料區 首先Java記憶體模型(JMM)和JVM執行時資料區並不是一個東西,許多介紹Java記憶體模型的文章描述的堆,方法區,Java虛擬機器棧,本地方法棧,程式計數器這東西並不是Java記憶體模型的內容而是JVM執行時資料區的內容。要理解二者的區別就要了解《Jav

java執行——執行之間的可見

目錄 一、簡介 一、簡介        我們知道執行緒在工作的時候有自己的私有記憶體,工作記憶體。程式執行的時候從主記憶體拉取需要的變數到工作記憶體,處理完再返回主記憶體。這篇文章總結哪些程式碼會使執行緒去主記憶體拉取變數。 二、volatile  

Java執行記憶體模型

##目錄 - 多執行緒需要解決的問題 - 執行緒之間的通訊 - 執行緒之間的同步 - Java記憶體模型 - 記憶體間的互動操作 - 指令屏障 - happens-before規則 - 指令重排序 - 從源程式到位元組指令的重排序 - as-if-serial語義 - 程式順序

java 執行執行狀態

java執行緒狀態 1. 初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。 2. 執行(RUNNABLE): Java執行緒中將就緒(ready)和執行中(running)兩種狀態籠統的稱為“執行”。 執行緒物件建立後,其他執行緒(比如main執行緒)呼叫了該物件

java執行記憶體 java 重排序 java happens-before java 記憶體語義

執行緒之間的通訊機制有兩種:           共享記憶體:在共享記憶體的併發模型裡,執行緒之間共享程式的公共狀態,通過寫-讀記憶體中的公共狀態進行隱式通訊。      

Java執行semaphore和Exchanger

Semaphore是Java執行緒的一個計數訊號量。我們可用於多執行緒的併發訪問控制。 就像我們常見的執行緒池,資料庫連線池就可以使用Semaphore進行邏輯的實現。Semaphore中我們就介紹兩個最常用的兩個方法。 acquire() 從Semaphore獲取許可,如果計數不小於0

Java執行非同步回撥(Callback)

●介紹      有時候執行一個任務需要很長時間,單執行緒下執行緒會處於阻塞狀態。這個時候我們會考慮一種非阻塞的處理模式。非阻塞任務在任何程式語言裡都必不可少,Java也不例外。多執行緒就是一個很好的解決辦法。     

java執行Latch設計模式見解

CountDownLatch :(個人理解)使用閥門值,直到閥門值為0之前,一直阻塞執行緒。實則使用物件鎖,不釋放物件鎖,一直佔用資源,(這裡是一個缺點)。閥門值為0時,呼叫釋放物件鎖的方法,釋放資源。應用的場景,我覺得是需要一些執行緒先完成的前提下,再使用其他執行緒。也就是我就是要一些重要的執行緒