マルチスレッドに぀いお知らなかった5぀のこず

マルチスレッドずそれをサポヌトするラむブラリを拒吊するJavaプログラマヌはほずんどいたせんが、問題を詳现に調査する時間を芋぀けた人はさらに少なくなりたす。 代わりに、特定のタスクに必芁なだけフロヌに぀いお孊習し、必芁な堎合にのみツヌルに新しいテクニックを远加したす。 したがっお、適切なアプリケヌションを䜜成しお実行するこずはできたすが、もっずうたくやるこずができたす。 コンパむラヌずJava仮想マシンの機胜を理解するず、より効率的で生産的なコヌドを䜜成するのに圹立ちたす。



5぀の事柄...シリヌズの今回の蚘事では、同期メ゜ッド、揮発性倉数、アトミッククラスなど、マルチスレッドプログラミングの埮劙な偎面をいく぀か玹介したす。 特に、これらの構成芁玠の䞀郚がJVMおよびJavaコンパむラず盞互䜜甚する方法、およびさたざたな盞互䜜甚がアプリケヌションのパフォヌマンスに䞎える圱響に぀いお説明したす。



翻蚳者泚私はマルチスレッドプログラミングに関するこれら5぀のこずを知らなかった人の1人であるため、この蚘事はここで公開する䟡倀があるず考えたしたが、翻蚳に間違いを犯す可胜性があるため、修正を歓迎したす熱意。

翻蚳者泚2コメントでは、知識のある人がトピックに関するリンクず情報を共有したす。蚘事の内容ず同じくらい面癜いです



1.同期メ゜ッドたたは同期ブロック



メ゜ッド党䜓を同期化するか、保護する必芁がある郚分のみを宣蚀するかに぀いお既に考えおいるかもしれたせん。 このような状況では、Javaコンパむラが゜ヌスコヌドをバむトコヌドに倉換するずきに、同期メ゜ッドず同期ブロックで非垞に異なる方法で動䜜するこずを知っおおくず圹立ちたす。



JVMが同期メ゜ッドを実行するず、実行䞭のスレッドは、このメ゜ッドのmethod_infoでACC_SYNCHRONIZEDフラグが蚭定されおいるず刀断したす。 次に、オブゞェクトに自動的にロックを蚭定し、メ゜ッドを呌び出し、ロックを解陀したす。 䟋倖がスロヌされるず、スレッドは自動的にロックを解陀したす。

䞀方、同期ブロックは、JVMに組み蟌たれたオブゞェクトロック芁求ず䟋倖凊理のサポヌトをバむパスするため、バむトコヌドで明瀺的に蚘述する必芁がありたす。 ブロックのバむトコヌドを芋るず、メ゜ッドず比范しお倚くの远加操䜜がブロック内にありたす。 リスト1は䞡方の呌び出しを瀺しおいたす。



リスト1.同期ぞの2぀のアプロヌチ。

パッケヌゞ com.geekcap ;

パブリック クラス SynchronizationExample {

private int i ;



public synchronized int synchronizedMethodGet   {

私を返す ;

}



public int synchronizedBlockGet   {

synchronized  this  {

私を返す ;

}

}

}


synchronizedMethodGetメ゜ッドは、次のバむトコヌドを生成したす。

  0aload_0
	 1getfield
	 2いいえ
	 3iconst_m1
	 4再発 


次に、synchronizedBlockGetメ゜ッドのバむトコヌドを瀺したす。

  0aload_0
	 1重耇
	 2astore_1
	 3monitorenter
	 4aload_0
	 5getfield
	 6nop
	 7iconst_m1
	 8aload_1
	 9monitorexit
	 10アむルランド
	 11astore_2
	 12aload_1
	 13monitorexit
	 14aload_2
	 15投げる 


同期ブロックを䜜成するず、16行のバむトコヌドが返されたしたが、同期メ゜ッドは5行のみを返したした。



2.「スレッド内」ThreadLocal倉数。



クラスのすべおのむンスタンスに察しお倉数の1぀のむンスタンスを保存する堎合は、静的クラス倉数を䜿甚したす。 各スレッドの倉数のむンスタンスを保存する堎合は、ThreadLocal倉数を䜿甚したす。 ThreadLocal倉数は、各スレッドが独自に個別に初期化された倉数のむンスタンス、getたたはsetメ゜ッドを介しおアクセスするずいう点で、通垞の倉数ず異なりたす。



コヌドを通る各スレッドのパスを䞀意に決定するこずを目暙ずするマルチスレッドコヌドトレヌサを開発しおいるずしたす。 問題は、耇数のスレッドにわたっお耇数のクラスの耇数のメ゜ッドを調敎する必芁があるこずです。 ThreadLocalがなければ、これは扱いにくいでしょう。 スレッドの実行が開始されたずき、ルヌタヌによる識別のための䞀意のマヌカヌを生成し、トレヌス時に各メ゜ッドにこのマヌカヌを枡す必芁がありたす。



ThreadLocalを䜿甚するず、これは簡単です。 スレッドは、実行の開始時にThreadLocal倉数を初期化しおから、各クラスの各メ゜ッドからアクセスしお、倉数は珟圚実行䞭のスレッドのみのトレヌス情報を保存したす。 その完了が完了するず、スレッドは、個々のトレヌスレコヌドを、すべおのレコヌドを維持する制埡オブゞェクトに転送できたす。



ThreadLocalの䜿甚は、各スレッドの倉数むンスタンスを保存する必芁がある堎合に意味がありたす。



3.揮発性倉数。



私の掚定では、Java開発者の半分だけがJavaにvolatileキヌワヌドがあるこずを知っおいたす。 これらのうち、その意味を知っおいるのは玄10だけであり、効果的な䜿い方を知っおいる人はさらに少ない。 ぀たり、volatileキヌワヌドを䜿甚した倉数の定矩は、倉数の倀が異なるスレッドによっお倉曎されるこずを意味したす。 volatileの意味を完党に理解するには、たず、スレッドが通垞の䞍揮発性倉数でどのように動䜜するかを理解する必芁がありたす。



パフォヌマンスを向䞊させるために、Java蚀語仕様では、JREは倉数を参照する各スレッドに倉数のロヌカルコピヌを保存できたす。 キャッシュに䌌た倉数のこれらの「むンラむン」コピヌを怜蚎できたす。これは、倉数の倀ぞのアクセスが必芁になるたびにメむンメモリをチェックするこずを回避するのに圹立ちたす。



しかし、次の堎合に䜕が起こるか想像しおみおください2぀のスレッドが開始し、最初のスレッドは倉数Aを5ずしお読み取り、2番目のスレッドは10ずしお読み取りたす。 Aの䞍正な倀。ただし、倉数Aがvolatileずしおマヌクされおいる堎合、スレッドがその倀にアクセスするずきはい぀でも、Aのコピヌを受け取り、珟圚の倀を読み取りたす。



アプリケヌションの倉数が倉曎されない堎合、むンラむンキャッシュが有効です。 それ以倖の堎合、volatileキヌワヌドが䜕をするこずができるかを知るこずは非垞に䟿利です。



4.揮発性vs同期。



倉数がvolatileずしお宣蚀されおいる堎合、耇数のスレッドを倉曎するこずが期埅されるこずを意味したす。 圓然、JREがvolatile倉数に䜕らかの圢の同期を課すず考えるでしょう。 良くも悪くも、JREはvolatile倉数にアクセスするずきに暗黙的に同期を提䟛したすが、1぀の非垞に倧きな泚意点がありたす。volatile倉数の読み取りは同期され、volatile倉数ぞの曞き蟌みは同期されたすが、非アトミック操䜜は同期されたせん。

぀たり、次のコヌドはスレッドセヌフではありたせん。

myVolatileVar ++;


このコヌドは次のようにも蚘述できたす。

int temp = 0 ;

同期 myVolatileVar  {

temp = myVolatileVar ;

}



temp ++;



同期 myVolatileVar  {

myVolatileVar = temp ;

}



぀たり、揮発性倉数が暗黙的に曎新される堎合、぀たり倀が読み取られ、倉曎され、新しい倀ずしお割り圓おられる堎合、結果は2぀の同期操䜜間で非スレッドセヌフになりたす。 同期を䜿甚するか、JREサポヌトに䟝存しお揮発性倉数を自動的に同期するかを遞択できたす。 最適なアプロヌチは、ケヌスによっお異なりたす。倉数に割り圓おられた揮発性倀が珟圚の倀に䟝存する堎合たずえば、むンクリメント操䜜䞭、操䜜をスレッドセヌフにする堎合は同期を䜿甚する必芁がありたす。



5.アトミックフィヌルドの曎新。



むンクリメントおよびデクリメント操䜜を実行するプリミティブ型が必芁な堎合、同期ブロックを自分で蚘述するよりも、java.util.concurrent.atomicパッケヌゞの新しいアトミッククラスから遞択する方がはるかに優れおいたす。 アトミッククラスは、特定の操䜜たずえば、増分操䜜ず枛分操䜜、曎新、倀の远加がスレッドセヌフであるこずを保蚌したす。 アトミッククラスのリストには、AtomicInteger、AtomicBoolean、AtomicLong、AtomicIntegerArrayなどが含たれたす。



アトミッククラスを䜿甚する際のプログラマにずっおの特有の課題は、操䜜のget、set、およびget-setファミリを含むすべおのクラス操䜜もアトミックであるこずです。 これは、アトミック倉数の倀を倉曎しない読み取りおよび曞き蟌み操䜜が同期され、重芁な読み取り/曎新/曞き蟌み操䜜だけではないこずを意味したす。 同期されたコヌドの展開をより詳现に制埡したい堎合、回避策はアトミックフィヌルドアップデヌタを䜿甚するこずです。

Atomic Updaterを䜿甚したす。


AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdaterなどのアトミックアップデヌタヌは、本質的に揮発性フィヌルドに適甚されるシェルです。 内郚では、Javaクラスラむブラリがそれらを䜿甚したす。 アプリケヌションコヌドではあたり䜿甚されたせんが、圌らの助けを借りお生掻を楜にし始めない理由はありたせん。



リスト2は、アトミック曎新を䜿甚しお、誰かが読んでいる本を倉曎するクラスの䟋を瀺しおいたす。

リスト2. Bookクラス。

パッケヌゞ com.geeckap.atomicexample ;



パブリック クラス ブック

{

プラむベヌト 文字列名;



公開 図曞  

{

}



公開 図曞  文字列名

{

これ 。 name = name ;

}



public String getName  

{

名前を返す ;

}



public void setName  文字列名

{

これ 。 name = name ;

}

}



Bookクラスは、ただ1぀のフィヌルドnameを持぀POJOプレヌンな叀いJavaオブゞェクト-プレヌンな叀いJavaオブゞェクトです。



リスト3. MyObjectクラス。

パッケヌゞ com.geeckap.atomicexample ;



import java.util.concurrent.atomic.AtomicReferenceFieldUpdater ;



/ **

*

* @author shaines

* /

パブリック クラス MyObject

{

プラむベヌト 揮発性 ブック whatImReading ;



private static final AtomicReferenceFieldUpdater < MyObject、Book > updater =

AtomicReferenceFieldUpdater。 newUpdater 

マむオブゞェクト クラス 、 ブック 。 クラス 、 「whatImReading」  ;



public Book getWhatImReading  

{

whatImReadingを返したす。

}



public void setWhatImReading  Book whatImReading 

{

//this.whatImReading = whatImReading;

アップデヌタヌ。 compareAndSet  これ 、 これ 。whatImReading 、whatImReading  ;

}

}



リスト3のMyObjectクラスは、ご想像のずおりgetメ゜ッドずsetメ゜ッドを衚しおいたすが、setメ゜ッドは別のこずを行いたす。 指定されたブックぞの内郚リンクを単に提䟛する代わりにリスト3のコメント化されたコヌドによっお行われたす、AtomicReferenceFieldUpdaterを䜿甚したす。



AtomicReferenceFieldUpdater


Javadocは、次のようにAtomicReferenceFieldUpdaterを定矩したす。



指定されたクラスの指定されたvolatile参照フィヌルドぞのアトミック曎新を可胜にするリフレクションベヌスのナヌティリティ。 このクラスは、同じノヌドの耇数の参照フィヌルドが独立しおアトミック曎新の察象ずなるアトミックデヌタ構造で䜿甚するために蚭蚈されおいたす。

割り圓おられたクラスの割り圓おられた揮発性参照フィヌルドぞのアトミック曎新を可胜にするリフレクションベヌスのナヌティリティ。このクラスは、同じレコヌドの耇数の参照フィヌルドがアトミック曎新の独立した゚ンティティであるアトミックデヌタ構造で䜿甚するためのものです これを普通に翻蚳する方法



リスト3では、3぀のパラメヌタヌを取るnewUpdaterメ゜ッドを呌び出すこずでAtomicReferenceFieldUpdaterが䜜成されたす。

•フィヌルドを含むオブゞェクトのクラスこの堎合、MyObject

•アトミックに曎新されるオブゞェクトのクラスこの堎合はBook

•アトミックアップデヌトのフィヌルド名



ここで重芁なのは、setWhatImReadingがアトミック操䜜ずしお実行されおいる間に、getWhatImReadingメ゜ッドが䜕らかの同期なしで実行されるこずです。



リスト4はsetWhatImReadingの䜿甚方法を瀺し、倉数が正しく倉曎されおいるこずを蚌明したす。



リスト4. Atomic Updaterのテストケヌス。

パッケヌゞ com.geeckap.atomicexample ;



import org.junit.Assert ;

import org.junit.Before ;

import org.junit.Test ;



パブリック クラス AtomicExampleTest

{

プラむベヌト MyObject obj ;



@Before

public void setUp  

{

obj = new MyObject   ;

obj。 setWhatImReading  新しい 本  「Java 2 From Scratch」   ;

}



@テスト

public void testUpdate  

{

obj。 setWhatImReading  new Book 

「Pro Java EE 5パフォヌマンス管理ず最適化」   ;

アサヌトしたす。 assertEquals  "誀ったブック名" 、

「Pro Java EE 5パフォヌマンス管理ず最適化」 、

obj。 getWhatImReading   。 getName    ;

}



}





結論ずしお。



マルチスレッドプログラミングは垞にテストですが、Javaプラットフォヌムが進化したため、䞀郚のマルチスレッドプログラミングタスクを簡玠化するサポヌトが埗られたした。 この蚘事では、同期メ゜ッドずコヌドブロックの違い、ThreadLocal倉数を䜿甚するこずの重芁性、揮発性に察する誀解揮発性に䟝存する危険性を含む同期を䜿甚する必芁がありたす、アトミッククラスの耇雑さの簡単な抂芁。 もっず知りたい人は、 リンクのセクション著者のサむトを参照しおください。



All Articles