国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

慕課網_《細說Java多線程之內存可見性》學習總結

wupengyu / 3263人閱讀

時間:2017年07月09日星期日
說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com
教學源碼:無
學習源碼:https://github.com/zccodere/s...

第一章:課程簡介 1-1 課程簡介

課程目標和學習內容

共享變量在線程間的可見性
synchronized實現可見性
volatile實現可見性
    指令重排序
    as-if-serial語義
    volatile使用注意事項
synchronized和volatile比較
第二章:可見性介紹 2-1 可見性介紹

可見性

一個線程對共享變量值的修改,能夠及時地被其他線程看到

共享變量

如果一個變量在多個線程的工作內存中都存在副本,那么這個變量就是這幾個線程的共享變量

Java內存模型(JMM)

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

Java內存模型

所有的變量都存儲在主內存中
每個線程都有自己獨立的工作內存,里面保存該線程使用到的變量的副本
(主內存中該變量的一份拷貝)

Java內存模型示意圖

Java內存模型中的兩條規定

線程對共享變量的所有操作都必須在自己的工作內存中進行,不能直接從主內存中讀寫
不同線程之間無法直接訪問其他線程工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成

共享變量可見性實現的原理。如線程1對共享變量的修改要想被線程2及時看到,必須要經過如下2個步驟

把工作內存1中更新過的共享變量刷新到主內存中
將主內存中最新的共享變量的值更新到工作內存2中

共享變量可見性實現的原理示意圖

第三章:synchronized實現可見性 3-1 synchronized實現可見性原理

要實現共享變量的可見性,必須保證兩點

線程修改后的共享變量能夠及時從工作內存刷新到主內存中
其他線程能夠及時把共享變量的最新值從主內存更新到自己的工作內存中

Java語言層面支持的可見性實現方式

synchronized
volatile

synchronized能夠實現

原子性(同步)
可見性

JMM關于synchronized的兩條規定

線程解鎖前,必須把共享變量的最新值刷新到主內存中
線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值
(注意:加鎖與解鎖需要時同一把鎖)

線程解鎖前對共享變量的修改在下次加鎖時對其他線程可見

線程執行互斥代碼的過程

1.獲得互斥鎖
2.清空工作內存
3.從主內存拷貝變量的最新副本到工作內存
4.執行代碼
5.將更改后的共享變量的值刷新到主內存
6.釋放互斥鎖

重排序

代碼書寫的順序與實際執行的順序不同,指令重排序是編譯器或處理器為了提高程序性能而做的優化

當前的三種重排序

編譯器優化的重排序(編譯器優化)
指令級并行重排序(處理器優化)
內存系統的重排序(處理器優化)

重排序示意圖:有可能出現下面情況

as-if-serial

無論如何重排序,程序執行的結果應該與代碼順序執行的結果一致(Java編譯器、運行時和處理器都會保證Java在單線程下遵循as-if-serial語義)

3-2 synchronized實現可見性(上)

代碼演示:

package com.myimooc.synchronizeddemo.my;

/**
 * 程序主類
 * @author ZhangCheng on 2017-07-09
 *
 */
public class SynchronizedDemo {
    
    //共享變量
    private boolean ready = false;
    private int result = 0;
    private int number = 1;   
    //寫操作
    public synchronized void write(){
        ready = true;                           //1.1                
        number = 2;                            //1.2                
    }
    //讀操作
    public synchronized void read(){                    
        if(ready){                             //2.1
            result = number*3;         //2.2
        }       
        System.out.println("result的值為:" + result);
    }

    //內部線程類
    private class ReadWriteThread extends Thread {
        //根據構造方法中傳入的flag參數,確定線程執行讀操作還是寫操作
        private boolean flag;
        public ReadWriteThread(boolean flag){
            this.flag = flag;
        }
        @Override                                                                    
        public void run() {
            if(flag){
                //構造方法中傳入true,執行寫操作
                write();
            }else{
                //構造方法中傳入false,執行讀操作
                read();
            }
        }
    }

    public static void main(String[] args)  {
        SynchronizedDemo synDemo = new SynchronizedDemo();
        //啟動線程執行寫操作
        synDemo.new ReadWriteThread(true).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //啟動線程執行讀操作
        synDemo.new ReadWriteThread(false).start();
    }
    
}
3-3 synchronized實現可見性(中)

導致共享變量在線程間不可見的原因

線程的交叉執行
重排序結合線程交叉執行
共享變量更新后的值沒有在工作內存與主內存間及時更新

安全的代碼

3-4 synchronized實現可見性(下)

第四章:volatile實現可見性 4-1 volatile能夠保證可見性

volatile關鍵字

能夠保證volatile變量的可見性
不能保證volatile變量復合操作的原子性

volatile如何實現內存可見性

深入來說:通過加入內存屏障和禁止重排序優化來實現的
    對volatile變量執行寫操作時,會在寫操作后加入一個store屏障指令
    對volatile變量執行讀操作時,會在讀操作前加入一條load屏障指令
通俗地講:volatile變量在每次被線程訪問時,都強迫從主內存中重讀該變量的值,
    而當該變量發生變化時,又會強迫線程將最新的值刷新到主內存。
    這樣任何時刻,不同的線程總能看到該變量的最新值

線程寫volatile變量的過程

改變線程工作內存中volatile變量副本的值
將改變后的副本的值從工作內存刷新到主內存

線程讀volatile變量的過程

從主內存中讀取volatile變量的最新值到線程的工作內存中
從工作內存中讀取volatile變量的副本
4-2 volatile不能保證原子性(上)

代碼演示:

package com.myimooc.volatiledemo.my;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 程序主類
 * @author ZhangCheng on 2017-07-09
 *
 */
public class VolatileDemo {
    
    private int number = 0;
    
    public int getNumber(){
        return this.number;
    }
    
    public void increase(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        this.number++;
    }
    
    public static void main(String[] args) {
        final VolatileDemo volDemo = new VolatileDemo();
        for(int i = 0 ; i < 500 ; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    volDemo.increase();
                }
            }).start();
        }
        
        //如果還有子線程在運行,主線程就讓出CPU資源,
        //直到所有的子線程都運行完了,主線程再繼續往下執行
        while(Thread.activeCount() > 1){
            Thread.yield();
        }
        
        System.out.println("number : " + volDemo.getNumber());
    }
    
}
4-3 volatile不能保證原子性(中)

程序分析

解決方案:保證number自增操作的原子性

使用synchronized關鍵字
使用ReentrantLock(java.util.concurrent.locks包下)
使用AtomicInterger(java.util.concurrent.atomic包下)
4-4 volatile不能保證原子性(下)

代碼演示:

package com.myimooc.volatiledemo.my;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 程序主類
 * @author ZhangCheng on 2017-07-09
 *
 */
public class VolatileDemo {
    
    private int number = 0;
    private Lock lock = new ReentrantLock();
    
    public int getNumber(){
        return this.number;
    }
    
    public void increase(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 方案一
        /*
        synchronized(this){
            this.number++;
        }
        */
        // 方案二
        lock.lock();// 獲取鎖
        try {
            this.number++;
        } finally {
            lock.unlock();// 釋放鎖
        }
        
    }
    
    public static void main(String[] args) {
        final VolatileDemo volDemo = new VolatileDemo();
        for(int i = 0 ; i < 500 ; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    volDemo.increase();
                }
            }).start();
        }
        
        //如果還有子線程在運行,主線程就讓出CPU資源,
        //直到所有的子線程都運行完了,主線程再繼續往下執行
        while(Thread.activeCount() > 1){
            Thread.yield();
        }
        
        System.out.println("number : " + volDemo.getNumber());
    }
    
}
4-5 volatile使用注意事項

volatile適用場合:要在多線程中安全的使用volatile變量,必須同時滿足

對變量的寫入操作不依賴其當前值
    不滿足:number++、count = count * 5等
    滿足:boolean變量、記錄溫度變化的變量等
該變量沒有包含在具有其他變量的不變式中
    不滿足:不變式low < up
4-6 synchronized和volatile比較

synchronized和volatile比較

volatile不需要加鎖,比synchronized更輕量級,不會阻塞線程
從內存可見性角度講,volatile讀相當于加鎖,volatile寫相當于解鎖
synchronized既能保證可見性,又能保證原子性,而volatile只能保證可見性,無法保證原子性
第五章:課程總結 5-1 課程總結

課程總結

什么是內存可見性
Java內存模型(JMM)
實現可見性的方式:synchronized和volatile
    final也可以保證內存可見性
synchronized和volatile實現內存可見性的原理
synchronized實現可見性
    指令重排序
    as-if-serial語義
volatile實現可見性
    volatile能夠保證可見性
    volatile不能保證原子性
    volatile使用注意事項
synchronized和volatile比較
    volatile比synchronized更輕量級
    volatile沒有synchronized使用的廣泛

問:即使沒有保證可見性的措施,很多時候共享變量依然能夠在主內存和工作內存見得到及時地更新?

答:一般只有在短時間內高并發的情況下才會出現變量得不到及時更新的情況,因為CPU在執行時會很快的刷新緩存,所以一般情況下很難看到這種情況。

對64位(long、double)變量的讀寫可能不是原子操作

Java內存模型允許JVM將沒有被volatile修飾的64位數據類型的讀寫操作劃分為兩次32位的讀寫操作來進行
導致問題:有可能會出現讀取到“半個變量”的情況
解決辦法:加volatile關鍵字

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67326.html

相關文章

  • 課網_細說線程Thread VS Runnable》學習總結

    摘要:時間年月日星期六說明本文部分內容均來自慕課網。慕課網教學源碼無學習源碼第一章課前準備前言課程說明比較和這兩種線程創建的方式,需要知道和的基本創建方式。一旦主線程獲取到了用戶的輸入,這時候,阻塞就會解除掉,主線程繼續運行,直到結束。 時間:2017年07月08日星期六說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學源碼:無學習源碼:https://g...

    netScorpion 評論0 收藏0
  • 課網_《Netty入門WebSocket初體驗》學習總結

    時間:2018年04月11日星期三 說明:本文部分內容均來自慕課網。@慕課網:https://www.imooc.com 教學源碼:https://github.com/zccodere/s... 學習源碼:https://github.com/zccodere/s... 第一章:課程介紹 1-1 課程介紹 什么是Netty 高性能、事件驅動、異步非阻塞的IO Java開源框架 基于NIO的客戶...

    Noodles 評論0 收藏0
  • 課網_Java定時任務調度工具詳解Timer篇》學習總結

    時間:2017年05月24日星期三說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:無個人學習源碼:https://github.com/zccodere/s... 第一章:課程介紹 1-1 課程介紹 什么是定時任務調度 基于給定的時間點,給定的時間間隔或者給定的執行次數自動執行的任務 在Java中的定時調度工具 Timer:小弟,能實現日常60%的定...

    wind5o 評論0 收藏0
  • 課網_《RxJava與RxAndroid基礎入門》學習總結

    時間:2017年10月16日星期一說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學源碼:無學習源碼:https://github.com/zccodere/s... 第一章:課程簡介 1-1 課程介紹 本門課程的主要內容 RxJava是什么 RxAndroid是什么 RxJava常用操作符(重點、難點) 怎樣在項目中使用RxJava和RxAndroid 如何學...

    劉明 評論0 收藏0
  • 課網_《Hibernate初探映射》學習總結

    時間:2017年07月11日星期二說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學源碼:無學習源碼:https://github.com/zccodere/s... 第一章:應用場景 1-1 多對多的應用場景 案例分析:企業項目開發過程中 一個項目可由多個員工參與開發 一個員工可同時參與開發多個項目 示意圖 showImg(https://segmentfau...

    caozhijian 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<