1. 程式人生 > >多執行緒讀書筆記二(java記憶體模型、volatile變數、記憶體模型與synchronized、CAS)

多執行緒讀書筆記二(java記憶體模型、volatile變數、記憶體模型與synchronized、CAS)

java記憶體模型 java中,執行緒之間的通訊是通過共享記憶體的方式,儲存在堆中的例項域,靜態域以及陣列元素都可以線上程間通訊。java記憶體模型控制一個執行緒對共享變數的改變何時對另一個執行緒可見。 執行緒間的共享變數存在主記憶體中,而對於每一個執行緒,都有一個私有的工作記憶體。工作記憶體是個虛擬的概念,涵蓋了快取,寫緩衝區,暫存器以及其他的硬體和編譯器優化,總之就是指執行緒的本地記憶體。存線上程本地記憶體中的變數值對其他執行緒是不可見的。 如果執行緒A與執行緒B之間如要通訊的話,必須要經歷下面2個步驟,如圖所示:
1. 首先,執行緒A把本地記憶體A中更新過的共享變數重新整理到主記憶體中去。
2. 然後,執行緒B到主記憶體中去讀取執行緒A之前已更新過的共享變數。  http://ifeve.com/wp-content/uploads/2013/01/221.png

關於volatile變數 由於java的記憶體模型中有工作記憶體和主記憶體之分,所以可能會有兩種問題: (1)執行緒可能在工作記憶體中更改變數的值,而沒有及時寫回到主記憶體,其他執行緒從主記憶體讀取的資料仍然是老資料 (2)執行緒在工作記憶體中更改了變數的值,寫回主記憶體了,但是其他執行緒之前也讀取了這個變數的值,這樣其他執行緒的工作記憶體中,此變數的值沒有被及時更新。 為了解決這個問題,可以使用同步機制,也可以把變數宣告為volatile,volatile修飾的成員變數有以下特點: (1)每次對變數的修改,都會引起處理器快取(工作記憶體)寫回到主記憶體。 (2)一個工作記憶體回寫到主記憶體會導致其他執行緒的處理器快取(工作記憶體)無效。
基於以上兩點,如果一個欄位被宣告成volatile,java執行緒記憶體模型確保所有執行緒看到這個變數的值是一致的。 此外,java虛擬機器規範(jvm spec)中,規定了宣告為volatile的long和double變數的get和set操作是原子的。這也說明了為什麼將long和double型別的變數用volatile修飾,就可以保證對他們的賦值操作的原子性了 關於volatile變數的使用建議:多執行緒環境下需要共享的變數採用volatile宣告;如果使用了同步塊或者是常量,則沒有必要使用volatile。 java記憶體模型與synchronized關鍵字 synchronized關鍵字強制實施一個互斥鎖,使得被保護的程式碼塊在同一時間只能有一個執行緒進入並執行。當然synchronized還有另外一個 方面的作用:線上程進入synchronized塊之前,會把工作存記憶體中的所有內容對映到主記憶體上,然後把工作記憶體清空再從主儲存器上拷貝最新的值。而 線上程退出synchronized塊時,同樣會把工作記憶體中的值對映到主記憶體,但此時並不會清空工作記憶體。這樣一來就可以強制其按照上面的順序執行,以 保證執行緒在執行完程式碼塊後,工作記憶體中的值和主記憶體中的值是一致的,保證了資料的一致性!  
所以由synchronized修飾的set與get方法都是相當於直接對主記憶體進行操作,不會出現資料一致性方面的問題。 關於CAS
CAS 操作包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B)。 如果記憶體位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新為新值 。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該 位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前 值。)CAS 有效地說明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。” 為什麼CAS可以用於同步? 例如,有一個變數i=0,Thread-1和Thread-2都對這個變數執行自增操作。 可能會出現Thread-1與Thread-2同時讀取i=0到各自的工作記憶體中,然後各自執行+1,最後將結果賦予i。這樣,雖然兩個執行緒都對i執行了自增操作,但是最後i的值為1,而不是2。 解決這個問題使用互斥鎖自然可以。但是也可以使用CAS來實現,思路如下: 自增操作可以分為兩步:(1)從記憶體中讀取這個變數的當前值(2)執行(變數=上一步取到的當前值+1)的賦值操作。 多執行緒情況下,自增操作出現問題的原因就是執行(2)的時候,變數在主記憶體中的值已經不等於上一步取到的當前值了,所以賦值時,用CompareAndSet操作代替Set操作:首先比較一下記憶體中這個變數的值是否等於上一步取到的當前值,如果等於,則說明可以執行+1運算,並賦值;如果不等於,則說明有其他執行緒在此期間更改了主記憶體中此變數的值,上一步取出的當前值已經失效,此時,不再執行+1運算及後續的賦值操作,而是返回主記憶體中此變數的最新值。“比較並交換(CAS)”操作是原子操作,它使用平臺提供的用於併發操作的硬體原語。

通過下面程式碼可以加深理解:
package com.jyq.multithread;

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

public class Counter {
	private AtomicInteger atomicInteger = new AtomicInteger(0);
	private int i = 0;

	// 使用CAS實現執行緒安全的計數器
	public void safeCount() {
		//用一個for迴圈,如果沒有計數成功的話,會一直執行這段程式碼,知道計數成功break為止
		for (;;) {
			int i = atomicInteger.get(); //讀取value值,賦給i,i線上程的工作記憶體中
			//將主記憶體中的值(current)與工作記憶體中的值i相比較,如果相等的話,說明工作記憶體中的i值仍然是value的最新值
			//計數運算對當前i操作沒有問題,將value值設為i+1,因為value是violent的,所以寫的時候也就寫到了主記憶體
			boolean suc = atomicInteger.compareAndSet(i, i + 1); 
			if (suc) {
				break;
			}
		}
	}

	// 非安全的執行緒計數器
	public void count() {
		i++;
	}

	public static void main(String[] args) {
		final Counter cas = new Counter();
		List<Thread> ts = new ArrayList<Thread>();
		for (int j = 0; j < 100; j++) {
			Thread t = new Thread(new Runnable() {

				@Override
				public void run() {
					for (int i = 0; i < 10000; i++) {
						cas.safeCount();
						cas.count();
					}

				}
			});
			ts.add(t);
		}
		for (Thread t : ts) {
			t.start();
		}
		// 等待所有執行緒執行完成
		for (Thread t : ts) {
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(cas.atomicInteger.get());
		System.out.println(cas.i);
	}
}

參考文章:
http://ifeve.com/java-memory-model-0/ 

深入理解java記憶體模型系列文章


http://ifeve.com/atomic-operation/

聊聊併發(五)原子操作的實現原理


相關推薦

執行讀書筆記java記憶體模型volatile變數記憶體模型synchronizedCAS)

java記憶體模型 java中,執行緒之間的通訊是通過共享記憶體的方式,儲存在堆中的例項域,靜態域以及陣列元素都可以線上程間通訊。java記憶體模型控制一個執行緒對共享變數的改變何時對另一個執行緒可見。 執行緒間的共享變數存在主記憶體中,而對於每一個執行緒,都有一個私有的工

Java執行學習筆記() synchronized同步方法-防止髒讀

1. 髒讀 在給一個物件賦值的時候進行了同步, 但是在取值的時候可能出現意外,此值已經被其他執行緒修改了,這種情況就是髒讀 1.1 PublicVar類 public class PublicVar { public String userName = "wang don

執行學習筆記之JUC元件

概述   為了對共享資源提供更細粒度的同步控制,JDK5新增了java.util.concurrent(JUC)併發工具包,併發包新增了Lock介面(以及相關實現類)用來實現鎖功能,它提供了與synchronized關鍵字相似的同步功能,只是在使用時需要顯式地獲取和釋放鎖,還具備內建鎖不具備的自由操作鎖

執行學習筆記--02物件及變數的併發訪問)

  1.學習目標          Synchronized物件監視器為Object時的使用      Synchronized物件監視器為Class時的使用    

執行學習筆記--執行

使用執行緒池的兩種方式:Runable介面和Callable介面 1.Runable介面實現步驟: 建立執行緒池物件 建立Runnable介面實現類 提交Runable介面實現類 關閉執行緒池(實際使用時一般不關閉,因為使用執行緒池就是為了減少執行緒的建立和銷燬)

趕緊收藏!王者級別的Java執行技術筆記,我java小菜雞願奉你為地表最強!

## Java多執行緒技術概述  介紹多執行緒之前要介紹執行緒,介紹執行緒則離不開程序。 首先 , 程序 :是一個正在執行中的程式,每一個程序執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元; 執行緒:就是程序中的一個獨立控制單元,執行緒在控制著程序的執行。一個程序中至少有一個程序。

Java 執行 學習筆記)停止執行的幾種方法

1.異常法: package test; import exthread.MyThread; import exthread.MyThread; public class Run { pu

Java執行程式設計實戰指南核心篇)讀書筆記四)

博主準備惡補一番Java高併發程式設計相關知識,接下來將閱讀該書,並且進行比較詳細的總結,好記性不如爛筆頭,加油。Java多執行緒程式設計實戰指南(核心篇)讀書筆記(四),主要記錄該書第七章和第八章的基

Java執行學習筆記四)

1、原子性:操作不能被打斷,要麼成功要麼失敗。i++ 不是原子操作。 2、可見性:一個執行緒修改了資料,其他執行緒立刻可見。 3、順序性: 4、volatile 保證程式的可見性,和順序性。不能保證對複合操作(如i++)的原子性。 javap -v xxx.class

Java執行之基礎篇

上一篇介紹了Java多執行緒的基礎概念和synchronized關鍵字,這篇繼續介紹Java多執行緒的其他關鍵字和重要的方法。 一、volatile關鍵字 1.1 Java記憶體模型

Java執行程式設計學習總結

  (尊重勞動成果,轉載請註明出處:https://blog.csdn.net/qq_25827845/article/details/84894463冷血之心的部落格) 系列文章: Java多執行緒程式設計學習總結(一) Java多執行緒程式設計學習總結(二) 前

Java執行學習筆記一)之中斷中的Interrupt,interrupted(),isInterrupted()

1、關於中斷 在Java中中斷最初是通過stop()來終止執行緒的,後來發現這樣簡單粗暴的停止執行緒會產生很多問題(例如物件monitor的釋放),所以改為deprecated,推薦使用interrupt()來中斷執行緒。而對於執行緒來說,會持有一個inter

java執行-專題-聊聊併發Java SE1.6中的Synchronized

1 引言 在多執行緒併發程式設計中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優化之後,有些情況下它並不那麼重了,本文詳細介紹了Java SE1.6中為了減少獲得鎖和釋放鎖帶來的效

java執行學習筆記高階)

1.程序和程序之間的記憶體是獨立的。 2.多程序的程式是為了提高CPU的使用率。 3.多執行緒不是為了提高執行速度,而是為了提高應用程式的使用率。 4.執行緒和執行緒共享“堆記憶體和方法區記憶體”,棧記憶體是獨立的也就是說一個執行緒一個棧。 5.關於java程式的執行原理:

Java 執行學習筆記十一) 單例設計模式延遲載入/懶漢模式)DCL解決執行安全問題

package extthread; import test.MyObject; public class MyThread extends Thread { @Override publi

Java 執行學習筆記五)synchronized 鎖重入

關鍵字synchronized 擁有鎖重入的功能,也就是使用synchronized時,當一個執行緒得到一個物件鎖後,再次請求此物件鎖時是可以再次得到該物件的鎖的。這也就證明在一個synchroniz

Java執行-併發工具類)等待執行完成的CountDownLatch

參考:https://www.jianshu.com/p/1716ce690637http://ifeve.com/talk-concurrency-countdownlatch/CountDownLatch是什麼CountDownLatch也叫閉鎖,在JDK1.5被引入,允

執行學習筆記)之執行安全問題

執行緒安全問題的現象 首先讓我們考慮一個問題: class Demo implements Runnable{ private int num = 100; //實現Runnable介面,覆蓋run方法 public void r

java執行同步以及執行間通訊詳解&amp;消費者生產者模式&amp;死鎖&amp;Thread.join()執行程式設計之

從執行結果,我們就可以看出我們4個售票視窗同時賣出了1號票,這顯然是不合邏輯的,其實這個問題就是我們前面所說的執行緒同步問題。不同的執行緒都對同一個資料進了操作這就容易導致資料錯亂的問題,也就是執行緒不同步。那麼這個問題該怎麼解決呢?在給出解決思路之前我們先來分析一下這個問題是怎麼產生的?我們宣告一個執行緒類

JAVA執行-Lock的使用)-公平鎖非公平鎖

公平鎖與非公平鎖        鎖Lock分為:公平鎖和非公平鎖。        公平鎖:表示執行緒獲取鎖的順序是按照執行緒加鎖的順序來分配的,即先來先得的FIFO先進先出順序。        非公平