日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java多线程:线程间通信之volatile与sychronized

發布時間:2023/12/1 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java多线程:线程间通信之volatile与sychronized 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

由前文Java內存模型我們熟悉了Java的內存工作模式和線程間的交互規范,本篇從應用層面講解Java線程間通信。

Java為線程間通信提供了三個相關的關鍵字volatile, synchronized和final。對于final,我們在博文Java中static關鍵字和final關鍵字中已經介紹。

  • 1. volatile
    • 1.1. 定義
    • 1.2. 機理
    • 1.3. 特性:不會被重排序
    • 1.4. 非原子性
  • 2. synchronized
    • 2.1. 定義
    • 2.2. synchronized與voliatile區別
    • 2.3. 注意
    • 2.4. synchronized的作用域
    • 2.5. synchronized應用
      • 2.5.1. synchronized方法
      • 2.5.2. synchronized代碼塊
      • 2.5.3. synchronized靜態方法
      • 2.5.4. synchronized對象
  • 3. 參考文章

1. volatile

1.1. 定義

由volatile定義的變量其特殊性在于:

一個線程對變量的寫一定對之后對這個變量的讀的線程可見。

換言之

一個線程對volatile變量的讀一定能看見它之前最后一個線程對這個變量的寫。

1.2. 機理

volatile意味著可見性,在講解volatile的機理前,我先給下面的這個例子:

package com.cielo.main;/*** Created by 63289 on 2017/3/31.*/ class MyThread extends Thread {private boolean isRunning = true;public boolean isRunning() {return isRunning;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run() {System.out.println("進入到run方法中了");while (isRunning == true) {}System.out.println("線程執行完成了");} } public class RunThread{public static void main(String[] args) {try {MyThread thread = new MyThread();thread.start();Thread.sleep(1000);thread.setRunning(false);} catch (InterruptedException e) {e.printStackTrace();}} }

在這個例子中,主線程啟動了子線程,子線程成功進入run方法,輸出”進入到run方法中”,只有由于isRunning==true,無限循環。此時,sleep一秒后的主線程想要改變isRunning的值,它將isRunning變量讀取到它的內存空間進行修改后,寫入主內存,但由于子線程一直在私有棧中讀取isRunning變量,沒有在主內存中讀取isRunning變量,因此不會退出循環。

如果我們把isRunning賦值行改為:

private volatile boolean isRunning = true;
將其用volatile修飾,則強制該變量從主內存中讀取。

這樣我們也就明白了volatile的實現機理,即:

  • 當一個線程要使用volatile變量時,它會直接從主內存中讀取,而不使用自己工作內存中的副本。

  • 當一個線程對一個volatile變量寫時,它會將變量的值刷新到共享內存(主內存)中。

  • 1.3. 特性:不會被重排序

    從Java內存模型一篇中,我們簡單了解了重排序,這里不會被重排序主要指語句重排序。

    我們考慮到下面這個例子,有A,B兩個線程

    線程A:加載配置文件,將配置元素初始化,之后標識初始化成功。

    Map configOptions ; char[] configText;volatile boolean initialized = false;//線程A首先從文件中讀取配置信息,調用process...處理配置信息,處理完成了將initialized 設置為true configOptions = new HashMap(); configText = readConfigFile(fileName); processConfig(configText, configOptions);//負責將配置信息configOptions 成功初始化 initialized = true; 線程B:等待初始化標識為true,之后開始工作。while(!initialized) {sleep(); }//使用配置信息干活 doSomethingWithConfig();

    很簡單的一個例子,在編譯器中,如果進行重排序,則會有將initialized=true這一行先執行的可能,如果這件事發生的話,線程B就會先運行,進而使用了沒有加載配置文件的Object。而如果initialized變量使用了volatile修飾,則編譯器不會將該變量的相關代碼進行重排序。(當然,這里的例子只是為了直觀,實際情況編譯器的重排序會更加復雜)

    1.4. 非原子性

    使用volatile時,我們要清楚,volatile是非原子性的。

    原子性即是指,對于一個操作,其操作的內容只有全部執行/全不執行兩個狀態,不存在中間態。而volatile并不能鎖定某組操作,防止其他線程的干擾,即沒有規定原子性,因而volatile是非原子性的?;蛘哒f,volatile是非線程安全的。

    綜上,如果我們想要使用一個原子性的修飾符來控制操作,即在操作變量時鎖定變量,我們就需要另一個修飾詞synchronized。

    2. synchronized

    2.1. 定義

    synchronized作用的代碼范圍對于不同線程是互斥的,并且線程在釋放鎖的時候會將共享變量的值刷新到共享內存中。

    2.2. synchronized與voliatile區別

  • 使用:voliatile 用于修飾變量,synchronized可以修飾對象,類,方法,代碼塊,語句。

  • 原子性:voliatile只保證變量的可見性,不能用于同步變量,即不保證原子性,多線程并發訪問voliatile修飾的變量時也不會產生阻塞。synchronized是原子性的,只有鎖定了變量的線程才能進入臨界區,從而保證臨界區的所有語句全部執行。多線程并發訪問sychronized修飾的變量會產生阻塞。

  • 機理:

  • 當線程對volatile變量讀時,會把工作內存中值置為無效。當線程對sychronized變量讀時,會在該線程鎖定變量時把工作內存中值置為無效。

    當線程對voliatile變量寫時,會把值刷新到主內存中。當線程對sychronized變量寫時,會在變量解鎖時把值刷新到主內存中。

    2.3. 注意

  • 無論synchronized加在方法上還是對象上,其修飾的都是對象,而不是方法或者某個代碼塊代碼語句。

  • 每個對象只有一個鎖與之相關聯。

  • 實現同步需要很大的系統開銷來做控制,不要做無謂的鎖定。

  • 2.4. synchronized的作用域

    synchronized的作用域只有兩種。實際上,synchronized直接作用于內存中的一個內存塊,因此,可以通過鎖定內存塊來鎖定一個實例變量或者鎖定一個靜態區域。

  • 某個對象實例內
  • synchronized aMethod(){}可以防止多個線程同時訪問這個對象的synchronized方法,如果對象有多個synchronized方法,則只要一個線程訪問了任何一個synchronized方法,其他線程不能同時訪問任何一個該對象的synchronized方法(synchronized作用于對象,且每個對象只有一個鎖)。

    顯然,不同對象的synchronized方法則不會互相影響(synchronized作用于對象)。

  • 某個類的范圍
  • 又或者說作用于靜態方法/靜態代碼塊。synchronized static aMethod(){}防止多個線程同時訪問這個類中的synchronized static方法,它可以對類的所有實例對象起作用。

    2.5. synchronized應用

    2.5.1. synchronized方法

    每個實例對應一個lock,線程獲得該含有synchronized方法的實例的鎖才可以執行,否則阻塞。方法一旦執行,則一直到方法返回才可以釋放鎖。此后被阻塞的線程才能獲得該鎖。對于一個實例,其聲明為synchronized的方法顯然只有一個能處于執行狀態。從而避免了類訪問變量的沖突。

    synchronized同步的開銷很大,如果synchronized作用于一個比較大的方法上,顯然是不合算的。

    2.5.2. synchronized代碼塊

    synchronized代碼塊形式如下:synchronized (synchronizedObject){//Some thing}

    代碼塊內部代碼必須在獲得synchronizedObject的鎖時才能執行。需要重點說的是synchronized(this),這也是比較常用的代碼塊。

    synchronized的效果類似于在方法前修飾,只是修飾的范圍縮小成代碼塊。兩個線程同時訪問一個變量時,如果一個線程在執行synchronized的代碼,那么該實例被鎖定,另一個線程如果要訪問該實例被synchronized作用的范圍,則會被阻塞。

    此外,如果不使用this作為鎖,而是只是想讓一段代碼同步,可以臨時創建如下鎖:

    private byte[] lock=new byte[0];

    從操作碼上講,創建一個長度為0的數組對象是最經濟的,只需要3條操作碼。

    2.5.3. synchronized靜態方法

    synchronized修飾靜態方法時或者在普通方法中以類為對象如下形式:

    class StaticSynchronized{public void aMethod{synchronized (StaticSynchronized.class){//Some thing}} }

    為synchronized靜態方法。

    注意的是,對于同一個類,其static和實例方法如果都用synchronized修飾,其作用的必然不是同一個對象(顯然)。

    2.5.4. synchronized對象

    比較簡單粗暴的實現方式,直接把對象鎖定,思路也很清晰。Java負責跟蹤被加鎖的對象,該鎖定對象的線程每次給對象加鎖時對象的計數器+1,每次解鎖時計數器-1,如果對象的計數器為0,那么解除該線程的鎖定。

    3. 參考文章

    如何使用 volatile, synchronized, final 進行線程間通信

    JAVA多線程之volatile 與 synchronized 的比較

    Java synchronized詳解

    ?

    轉載于:https://www.cnblogs.com/cielosun/p/6650161.html

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的Java多线程:线程间通信之volatile与sychronized的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。