亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

舉例解析Java多線程編程中需要注意的一些關(guān)鍵點

 更新時間:2015年11月23日 15:10:23   作者:turkeyzhou  
這篇文章主要介紹了Java多線程編程中需要注意的一些關(guān)鍵點,包括ThreadLocal變量與原子更新等一些深層次的內(nèi)容,需要的朋友可以參考下

1. 同步方法或同步代碼塊?
您可能偶爾會思考是否要同步化這個方法調(diào)用,還是只同步化該方法的線程安全子集。在這些情況下,知道 Java 編譯器何時將源代碼轉(zhuǎn)化為字節(jié)代碼會很有用,它處理同步方法和同步代碼塊的方式完全不同。
當 JVM 執(zhí)行一個同步方法時,執(zhí)行中的線程識別該方法的 method_info 結(jié)構(gòu)是否有 ACC_SYNCHRONIZED 標記設(shè)置,然后它自動獲取對象的鎖,調(diào)用方法,最后釋放鎖。如果有異常發(fā)生,線程自動釋放鎖。
另一方面,同步化一個方法塊會越過 JVM 對獲取對象鎖和異常處理的內(nèi)置支持,要求以字節(jié)代碼顯式寫入功能。如果您使用同步方法讀取一個方法的字節(jié)代碼,就會看到有十幾個額外的操作用于管理這個功能。清單 1 展示用于生成同步方法和同步代碼塊的調(diào)用:

清單 1. 兩種同步化方法
    

package com.geekcap;

public class SynchronizationExample {
  private int i;

  public synchronized int synchronizedMethodGet() {
    return i;
  }

  public int synchronizedBlockGet() {
    synchronized( this ) {
      return i;
    }
  }
}

synchronizedMethodGet() 方法生成以下字節(jié)代碼:

 0: aload_0
 1: getfield
 2: nop
 3: iconst_m1
 4: ireturn

這里是來自 synchronizedBlockGet() 方法的字節(jié)代碼:

 0: aload_0
 1: dup
 2: astore_1
 3: monitorenter
 4: aload_0
 5: getfield
 6: nop
 7: iconst_m1
 8: aload_1
 9: monitorexit
 10: ireturn
 11: astore_2
 12: aload_1
 13: monitorexit
 14: aload_2
 15: athrow

創(chuàng)建同步代碼塊產(chǎn)生了 16 行的字節(jié)碼,而創(chuàng)建同步方法僅產(chǎn)生了 5 行。
回頁首
2. ThreadLocal 變量
如果您想為一個類的所有實例維持一個變量的實例,將會用到靜態(tài)類成員變量。如果您想以線程為單位維持一個變量的實例,將會用到線程局部變量。ThreadLocal 變量與常規(guī)變量的不同之處在于,每個線程都有其各自初始化的變量實例,這通過 get() 或 set() 方法予以評估。
比方說您在開發(fā)一個多線程代碼跟蹤器,其目標是通過您的代碼惟一標識每個線程的路徑。挑戰(zhàn)在于,您需要跨多個線程協(xié)調(diào)多個類中的多個方法。如果沒有 ThreadLocal,這會是一個復雜的問題。當一個線程開始執(zhí)行時,它需要生成一個惟一的令牌來在跟蹤器中識別它,然后將這個惟一的令牌傳遞給跟蹤中的每個方法。
使用 ThreadLocal,事情就變得簡單多了。線程在開始執(zhí)行時初始化線程局部變量,然后通過每個類的每個方法訪問它,保證變量將僅為當前執(zhí)行的線程托管跟蹤信息。在執(zhí)行完成之后,線程可以將其特定的蹤跡傳遞給一個負責維護所有跟蹤的管理對象。
當您需要以線程為單位存儲變量實例時,使用 ThreadLocal 很有意義。

3. Volatile 變量
我估計,大約有一半的 Java 開發(fā)人員知道 Java 語言包含 volatile 關(guān)鍵字。當然,其中只有 10% 知道它的確切含義,有更少的人知道如何有效使用它。簡言之,使用 volatile 關(guān)鍵字識別一個變量,意味著這個變量的值會被不同的線程修改。要完全理解 volatile關(guān)鍵字的作用,首先應(yīng)當理解線程如何處理非易失性變量。
為了提高性能,Java 語言規(guī)范允許 JRE 在引用變量的每個線程中維護該變量的一個本地副本。您可以將變量的這些 “線程局部” 副本看作是與緩存類似,在每次線程需要訪問變量的值時幫助它避免檢查主存儲器。
不過看看在下面場景中會發(fā)生什么:兩個線程啟動,第一個線程將變量 A 讀取為 5,第二個線程將變量 A 讀取為 10。如果變量 A 從 5 變?yōu)?10,第一個線程將不會知道這個變化,因此會擁有錯誤的變量 A 的值。但是如果將變量 A 標記為 volatile,那么不管線程何時讀取 A 的值,它都會回頭查閱 A 的原版拷貝并讀取當前值。
如果應(yīng)用程序中的變量將不發(fā)生變化,那么一個線程局部緩存比較行得通。不然,知道 volatile 關(guān)鍵字能為您做什么會很有幫助。
4. 易失性變量與同步化
如果一個變量被聲明為 volatile,這意味著它預(yù)計會由多個線程修改。當然,您會希望 JRE 會為易失性變量施加某種形式的同步。幸運的是,JRE 在訪問易失性變量時確實隱式地提供同步,但是有一條重要提醒:讀取易失性變量是同步的,寫入易失性變量也是同步的,但非原子操作不同步。
這表示下面的代碼不是線程安全的:

myVolatileVar++;

上一條語句也可寫成:

int temp = 0;
synchronize( myVolatileVar ) {
 temp = myVolatileVar;
}

temp++;

synchronize( myVolatileVar ) {
 myVolatileVar = temp;
}

換言之,如果一個易失性變量得到更新,這樣其值就會在底層被讀取、修改并分配一個新值,結(jié)果將是一個在兩個同步操作之間執(zhí)行的非線程安全操作。然后您可以決定是使用同步化還是依賴于 JRE 的支持來自動同步易失性變量。更好的方法取決于您的用例:如果分配給易失性變量的值取決于當前值(比如在一個遞增操作期間),要想該操作是線程安全的,那么您必須使用同步化。
5. 原子字段更新程序
在一個多線程環(huán)境中遞增或遞減一個原語類型時,使用在 java.util.concurrent.atomic 包中找到的其中一個新原子類比編寫自己的同步代碼塊要好得多。原子類確保某些操作以線程安全方式被執(zhí)行,比如遞增和遞減一個值,更新一個值,添加一個值。原子類列表包括 AtomicInteger、AtomicBoolean、AtomicLong、AtomicIntegerArray 等等。
使用原子類的難題在于,所有類操作,包括 get、set 和一系列 get-set 操作是以原子態(tài)呈現(xiàn)的。這表示,不修改原子變量值的 read和 write 操作是同步的,不僅僅是重要的 read-update-write 操作。如果您希望對同步代碼的部署進行更多細粒度控制,那么解決方案就是使用一個原子字段更新程序。
使用原子更新
像 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater 之類的原子字段更新程序基本上是應(yīng)用于易失性字段的封裝器。Java 類庫在內(nèi)部使用它們。雖然它們沒有在應(yīng)用程序代碼中得到廣泛使用,但是也沒有不能使用它們的理由。
清單 2 展示一個有關(guān)類的示例,該類使用原子更新來更改某人正在讀取的書目:

清單 2. Book 類
    

package com.geeckap.atomicexample;

public class Book
{
  private String name;

  public Book()
  {
  }

  public Book( String name )
  {
    this.name = name;
  }

  public String getName()
  {
    return name;
  }

  public void setName( String name )
  {
    this.name = name;
  }
}

Book 類僅是一個 POJO(Java 原生類對象),擁有一個單一字段:name。

清單 3. MyObject 類
    

package com.geeckap.atomicexample;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 *
 * @author shaines
 */
public class MyObject
{
  private volatile Book whatImReading;

  private static final AtomicReferenceFieldUpdater<MyObject,Book> updater =
      AtomicReferenceFieldUpdater.newUpdater( 
            MyObject.class, Book.class, "whatImReading" );

  public Book getWhatImReading()
  {
    return whatImReading;
  }

  public void setWhatImReading( Book whatImReading )
  {
    //this.whatImReading = whatImReading;
    updater.compareAndSet( this, this.whatImReading, whatImReading );
  }
}

正如您所期望的,清單 3 中的 MyObject 類通過 get 和 set 方法公開其 whatAmIReading 屬性,但是 set 方法所做的有點不同。它不僅僅將其內(nèi)部 Book 引用分配給指定的 Book(這將使用 清單 3 中注釋出的代碼來完成),而是使用一個AtomicReferenceFieldUpdater。
AtomicReferenceFieldUpdater
AtomicReferenceFieldUpdater 的 Javadoc 將其定義為:
對指定類的指定易失性引用字段啟用原子更新的一個基于映像的實用程序。該類旨在用于這樣的一個原子數(shù)據(jù)結(jié)構(gòu)中:即同一節(jié)點的若干引用字段獨立地得到原子更新。
在 清單 3 中,AtomicReferenceFieldUpdater 由一個對其靜態(tài) newUpdater 方法的調(diào)用創(chuàng)建,該方法接受三個參數(shù):
包含字段的對象的類(在本例中為 MyObject)
將得到原子更新的對象的類(在本例中是 Book)
將經(jīng)過原子更新的字段的名稱
這里真正的價值在于,getWhatImReading 方法未經(jīng)任何形式的同步便被執(zhí)行,而 setWhatImReading 是作為一個原子操作執(zhí)行的。
清單 4 展示如何使用 setWhatImReading() 方法并斷定值的變動是正確的:

清單 4. 演習原子更新的測試用例
    

package com.geeckap.atomicexample;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class AtomicExampleTest
{
  private MyObject obj;

  @Before
  public void setUp()
  {
    obj = new MyObject();
    obj.setWhatImReading( new Book( "Java 2 From Scratch" ) );
  }

  @Test
  public void testUpdate()
  {
    obj.setWhatImReading( new Book( 
        "Pro Java EE 5 Performance Management and Optimization" ) );
    Assert.assertEquals( "Incorrect book name", 
        "Pro Java EE 5 Performance Management and Optimization", 
        obj.getWhatImReading().getName() );
  }

}


結(jié)束語
多線程編程永遠充滿了挑戰(zhàn),但是隨著 Java 平臺的演變,它獲得了簡化一些多線程編程任務(wù)的支持。在本文中,我討論了關(guān)于在 Java 平臺上編寫多線程應(yīng)用程序您可能不知道的 5 件事,包括同步化方法與同步化代碼塊之間的不同,為每個線程存儲運用ThreadLocal 變量的價值,被廣泛誤解的 volatile 關(guān)鍵字(包括依賴于 volatile 滿足同步化需求的危險),以及對原子類的錯雜之處的一個簡要介紹。參見 參考資料 部分了解更多內(nèi)容。

相關(guān)文章

  • SpringBoot?SpringSecurity?JWT實現(xiàn)系統(tǒng)安全策略詳解

    SpringBoot?SpringSecurity?JWT實現(xiàn)系統(tǒng)安全策略詳解

    Spring?Security是Spring的一個核心項目,它是一個功能強大且高度可定制的認證和訪問控制框架。它提供了認證和授權(quán)功能以及抵御常見的攻擊,它已經(jīng)成為保護基于spring的應(yīng)用程序的事實標準
    2022-11-11
  • spring?cloud?eureka?服務(wù)啟動失敗的原因分析及解決方法

    spring?cloud?eureka?服務(wù)啟動失敗的原因分析及解決方法

    這篇文章主要介紹了spring?cloud?eureka?服務(wù)啟動失敗的原因解析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • Spring?Cloud灰度部署實現(xiàn)過程詳解

    Spring?Cloud灰度部署實現(xiàn)過程詳解

    這篇文章主要為大家介紹了Spring?Cloud灰度部署實現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Spring中的SpringData詳細說明

    Spring中的SpringData詳細說明

    這篇文章主要介紹了Spring中的SpringData詳細說明,Spring Data 是Spring 的一個子項目, 旨在統(tǒng)一和簡化對各類型持久化存儲, 而不拘泥于是關(guān)系型數(shù)據(jù)庫還是NoSQL 數(shù)據(jù)存儲,需要的朋友可以參考下
    2023-11-11
  • MyBatis?多表聯(lián)合查詢及優(yōu)化方法

    MyBatis?多表聯(lián)合查詢及優(yōu)化方法

    大家都知道Hibernate 是全自動的數(shù)據(jù)庫持久層框架,它可以通過實體來映射數(shù)據(jù)庫,通過設(shè)置一對多、多對一、一對一、多對多的關(guān)聯(lián)來實現(xiàn)聯(lián)合查詢,接下來通過本文給大家介紹MyBatis?多表聯(lián)合查詢及優(yōu)化,需要的朋友可以參考下
    2022-08-08
  • 舉例解析Java多線程編程中需要注意的一些關(guān)鍵點

    舉例解析Java多線程編程中需要注意的一些關(guān)鍵點

    這篇文章主要介紹了Java多線程編程中需要注意的一些關(guān)鍵點,包括ThreadLocal變量與原子更新等一些深層次的內(nèi)容,需要的朋友可以參考下
    2015-11-11
  • 淺談兩個jar包中包含完全相同的包名和類名的加載問題

    淺談兩個jar包中包含完全相同的包名和類名的加載問題

    下面小編就為大家?guī)硪黄獪\談兩個jar包中包含完全相同的包名和類名的加載問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • 淺談mac下maven的安裝配置與使用

    淺談mac下maven的安裝配置與使用

    這篇文章主要介紹了淺談mac下maven的安裝配置與使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 深入解析反編譯字節(jié)碼文件中的代碼邏輯JVM中的String操作

    深入解析反編譯字節(jié)碼文件中的代碼邏輯JVM中的String操作

    這篇文章主要介紹了深入解析反編譯字節(jié)碼文件中的代碼邏輯JVM中的String操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • 關(guān)于mybatis if else if 條件判斷SQL片段表達式取值和拼接問題

    關(guān)于mybatis if else if 條件判斷SQL片段表達式取值和拼接問題

    這篇文章主要介紹了mybatis if else if 條件判斷SQL片段表達式取值和拼接,文章通過自己真實使用的例子給大家詳細介紹,需要的朋友可以參考下
    2021-09-09

最新評論