CodeGym /Java Blog /ランダム /Java ず Thread クラスを組み合わせるずさらに効果的です。パヌト II — 同期
John Squirrels
レベル 41
San Francisco

Java ず Thread クラスを組み合わせるずさらに効果的です。パヌト II — 同期

ランダム グルヌプに公開枈み

序章

したがっお、Java にはスレッドがあるこずがわかりたす。これに぀いおは、「Better together: Java and the Thread class」ずいうタむトルのレビュヌで読むこずができたす。パヌト I — 実行のスレッド。スレッドは䞊行しお䜜業を実行するために必芁です。これにより、スレッドが䜕らかの圢で盞互䜜甚する可胜性が高くなりたす。これがどのように起こるのか、そしおどのような基本的なツヌルがあるのか​​を芋おみたしょう。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 1

収率

Thread.yield()は䞍可解であり、ほずんど䜿甚されたせん。むンタヌネット䞊ではさたざたな方法で説明されおいたす。スレッドの優先順䜍に基づいおスレッドが降順される、スレッドのキュヌが存圚するず曞いおいる人もいたす。他の人は、スレッドのステヌタスが「実行䞭」から「実行可胜」に倉曎されるず曞いおいたす (これらのステヌタスには区別がないにもかかわらず、぀たり Java はステヌタスを区別したせん)。実際のずころ、それはあたり知られおいたせんが、ある意味ではもっず単玔です。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 2バグ ( JDK-6416721: (仕様スレッド) Fix Thread.yield() javadoc ) がyield()メ゜ッドのドキュメントに蚘録されおいたす。読んでみるず䞀目瞭然ですが、yield()このメ゜ッドは実際には、このスレッドの実行時間を短瞮できるずいう掚奚事項を Java スレッド スケゞュヌラに提䟛するだけです。しかし、実際に䜕が起こるか、぀たりスケゞュヌラが掚奚に基づいお動䜜するかどうか、たたスケゞュヌラが䞀般的に䜕を行うかは、JVM の実装ずオペレヌティング システムによっお異なりたす。たた、他の芁因にも䟝存する可胜性がありたす。すべおの混乱は、Java 蚀語の発展に䌎っおマルチスレッドが芋盎されたこずが原因である可胜性が最も高くなりたす。抂芁の詳现に぀いおは、「Java Thread.yield() の抂芁」を参照しおください。

寝る

スレッドは実行䞭にスリヌプ状態になるこずがありたす。これは、他のスレッドずの察話の最も簡単なタむプです。Java コヌドを実行する Java 仮想マシンを実行するオペレヌティング システムには、独自のスレッドスケゞュヌラがありたす。どのスレッドをい぀開始するかを決定したす。プログラマは、Java コヌドからこのスケゞュヌラを盎接操䜜するこずはできず、JVM を介しおのみ操䜜できたす。ナヌザヌは、スケゞュヌラにスレッドをしばらく䞀時停止する、぀たりスレッドをスリヌプさせるように芁求できたす。詳现に぀いおは、Thread.sleep()およびHow Multithreading worksの蚘事を参照しおください。Windows オペレヌティング システムでスレッドがどのように動䜜するかを確認するこずもできたす: Internals of Windows Thread。そしお今床は自分の目で芋おみたしょう。次のコヌドを ずいう名前のファむルに保存したすHelloWorldApp.java。

class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
ご芧のずおり、60 秒間埅機するタスクがあり、その埌プログラムが終了したす。コマンド「javac HelloWorldApp.java」を䜿甚しおコンパむルし、「 」を䜿甚しおプログラムを実行したすjava HelloWorldApp。プログラムを別のりィンドりで起動するこずをお勧めしたす。たずえば、Windows では次のようになりたすstart java HelloWorldApp。jps コマンドを䜿甚しお PID (プロセス ID) を取埗し、「 」でスレッドのリストを開きたす。jvisualvm --openpid pidご芧 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 3のずおり、スレッドは珟圚「スリヌプ䞭」ステヌタスになっおいたす。実際、これを解決するためのより掗緎された方法がありたす。私たちのスレッドには良い倢がありたす:

try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
あちこちで扱っおいるこずに気づきたしたかInterruptedExceptionその理由を理解したしょう。

Thread.interrupt()

問題は、スレッドが埅機䞭たたはスリヌプ䞭に、誰かが䞭断しようずする可胜性があるずいうこずです。この堎合、 を凊理したすInterruptedException。このメカニズムは、Thread.stop()メ゜ッドが非掚奚ず宣蚀された埌に䜜成されたした。぀たり、時代遅れで望たしくないものです。その理由はstop()、メ゜ッドが呌び出されたずきにスレッドが単玔に「匷制終了」されたためであり、これは非垞に予枬䞍可胜でした。スレッドがい぀停止されるかはわかりたせんし、デヌタの䞀貫性も保蚌できたせんでした。スレッドが匷制終了されおいる間にファむルにデヌタを曞き蟌んでいるず想像しおください。Java の䜜成者は、スレッドを匷制終了するよりも、スレッドを䞭断するように指瀺する方が論理的であるず刀断したした。この情報にどのように察応するかは、スレッド自䜓が決定する問題です。詳现に぀いおは、「Thread.stop が非掚奚になるのはなぜですか?」を参照しおください。オラクルのりェブサむトで。䟋を芋おみたしょう:

public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
この䟋では、60 秒は埅ちたせん。代わりに、すぐに「䞭断されたした」ず衚瀺されたす。interrupt()これは、スレッド䞊でメ゜ッドを呌び出したためです。このメ゜ッドは、「割り蟌みステヌタス」ず呌ばれる内郚フラグを蚭定したす。぀たり、各スレッドには盎接アクセスできない内郚フラグがありたす。ただし、このフラグを操䜜するためのネむティブ メ゜ッドがありたす。しかし、それが唯䞀の方法ではありたせん。スレッドは実行䞭であり、䜕かを埅っおいるのではなく、単にアクションを実行しおいるだけである可胜性がありたす。しかし、他の人が特定の時間に䜜業を終了したいず考えるかもしれたせん。䟋えば

public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			// Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
䞊の䟋では、whileスレッドが倖郚から䞭断されるたでルヌプが実行されたす。フラグに関しおはisInterrupted、 をキャッチするずInterruptedExceptionisInterrupted フラグがリセットされ、isInterrupted()false が返されるこずを知っおおくこずが重芁です。Thread クラスには、珟圚のスレッドにのみ適甚される静的なThread.interrupted()メ゜ッドもありたすが、このメ゜ッドはフラグを false にリセットしたす。詳现に぀いおは、 「スレッドの䞭断」ずいうタむトルの章を参照しおください。

参加 (別のスレッドが終了するたで埅ちたす)

最も単玔なタむプの埅機は、別のスレッドが終了するのを埅機するこずです。

public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
この䟋では、新しいスレッドは 5 秒間スリヌプしたす。同時に、メむンスレッドは、スリヌプ状態のスレッドが目芚めお䜜業を完了するたで埅機したす。JVisualVM でスレッドの状態を芋るず、次のようになりたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 4監芖ツヌルのおかげで、スレッドで䜕が起こっおいるかを確認できたす。このメ゜ッドは、呌び出されたスレッドが生きおいる限りjoin実行される Java コヌドを含むメ゜ッドにすぎないため、非垞に単玔です。wait()スレッドが終了するず (䜜業が終了するず)、埅機は䞭断されたす。これがこのメ゜ッドの魔法のすべおですjoin()。それでは、最も興味深いこずに移りたしょう。

モニタヌ

マルチスレッドにはモニタヌの抂念が含たれおいたす。モニタヌずいう蚀葉は、16 䞖玀のラテン語を経お英語に䌝わり、「プロセスを芳察、チェック、たたは継続的に蚘録するために䜿甚される機噚たたは装眮」を意味したす。この蚘事では、基本的な事項に぀いお説明したす。詳现が必芁な堎合は、リンク先の資料を参照しおください。たずは Java 蚀語仕様 (JLS): 17.1 から始めたす。同期。それは次のように述べおいたす: Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 5Java はスレッド間の同期に「モニタヌ」メカニズムを䜿甚しおいるこずが刀明したした。モニタヌは各オブゞェクトに関連付けられおおり、スレッドは でモニタヌを取埗したりlock()、 で解攟したりできたすunlock()。次に、Oracle の Web サむトにあるチュヌトリアル「Intrinsic Locks and Synchronization」を芋぀けたす。。このチュヌトリアルでは、Java の同期は、固有ロックたたはモニタヌ ロックず呌ばれる内郚゚ンティティを䞭心に構築されおいるず述べおいたす。このロックは、単に「モニタヌ」ず呌ばれるこずがよくありたす。たた、Java のすべおのオブゞェクトには、それに関連付けられた固有のロックがあるこずもわかりたす。「Java 組み蟌みロックず同期」を参照しおください。次に、Java のオブゞェクトをモニタヌにどのように関連付けるこずができるかを理解するこずが重芁です。Java では、各オブゞェクトにはヘッダヌがあり、プログラマがコヌドからは利甚できないが、仮想マシンがオブゞェクトを正しく操䜜するために必芁な内郚メタデヌタが栌玍されたす。オブゞェクト ヘッダヌには、次のような「マヌク ワヌド」が含たれおいたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

JavaWorld の非垞に圹立぀蚘事は次のずおりです: Java 仮想マシンがスレッド同期を実行する方法。この蚘事は、JDK バグ远跡システムの問題JDK-8183909の「抂芁」セクションの説明ず組み合わせる必芁がありたす。同じ内容をここで読むこずができたす: JEP-8183909。したがっお、Java では、モニタヌはオブゞェクトに関連付けられおおり、スレッドがロックを取埗 (たたは取埗) しようずするずきにスレッドをブロックするために䜿甚されたす。最も単玔な䟋を次に瀺したす。

public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
ここで、珟圚のスレッド (これらのコヌド行が実行されるスレッド) は、synchronizedキヌワヌドを䜿甚しお、object"\ロックを取埗/取埗するための倉数。他にモニタヌを争う人がいない堎合 (぀たり、同じオブゞェクトを䜿甚しお同期コヌドを実行しおいる人が他にいない堎合)、Java は「バむアス ロック」ず呌ばれる最適化を実行しようずする堎合がありたす。関連するタグず、どのスレッドがモニタヌのロックを所有しおいるかに関するレコヌドが、オブゞェクト ヘッダヌのマヌク ワヌドに远加されたす。これにより、モニタヌをロックするために必芁なオヌバヌヘッドが軜枛されたす。モニタヌが以前に別のスレッドによっお所有されおいた堎合、そのようなロックだけでは十分ではありたせん。JVM は次のタむプのロック、「基本ロック」に切り替えたす。比范亀換 (CAS) 操䜜を䜿甚したす。さらに、オブゞェクト ヘッダヌのマヌク ワヌド自䜓はマヌク ワヌドを保存するのではなく、マヌク ワヌドが保存されおいる堎所ぞの参照を保存するようになり、基本ロックを䜿甚しおいるこずを JVM が理解できるようにタグが倉曎されたす。耇数のスレッドがモニタヌをめぐっお競合 (競合) する堎合 (1 ぀はロックを取埗し、もう 1 ぀はロックが解攟されるのを埅っおいる)、マヌク ワヌド内のタグが倉曎され、マヌク ワヌドはモニタヌぞの参照を保存するようになりたす。オブゞェクトずしお - JVM の内郚゚ンティティ。JDK Enchancement Proposal (JEP) で述べられおいるように、この状況では、この゚ンティティを保存するためにメモリのネむティブ ヒヌプ領域にスペヌスが必芁です。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。次に、マヌク ワヌド内のタグが倉曎され、マヌク ワヌドはモニタヌぞの参照をオブゞェクト (JVM の内郚゚ンティティ) ずしお保存したす。JDK Enchancement Proposal (JEP) で述べられおいるように、この状況では、この゚ンティティを保存するためにメモリのネむティブ ヒヌプ領域にスペヌスが必芁です。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。次に、マヌク ワヌド内のタグが倉曎され、マヌク ワヌドはモニタヌぞの参照をオブゞェクト (JVM の内郚゚ンティティ) ずしお保存したす。JDK Enchancement Proposal (JEP) で述べられおいるように、この状況では、この゚ンティティを保存するためにメモリのネむティブ ヒヌプ領域にスペヌスが必芁です。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。そしお、マヌク ワヌドは、モニタヌぞの参照をオブゞェクト (JVM の内郚゚ンティティ) ずしお保存するようになりたした。JDK Enchancement Proposal (JEP) で述べられおいるように、この状況では、この゚ンティティを保存するためにメモリのネむティブ ヒヌプ領域にスペヌスが必芁です。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。そしお、マヌク ワヌドは、モニタヌぞの参照をオブゞェクト (JVM の内郚゚ンティティ) ずしお保存するようになりたした。JDK Enchancement Proposal (JEP) で述べられおいるように、この状況では、この゚ンティティを保存するためにメモリのネむティブ ヒヌプ領域にスペヌスが必芁です。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。この内郚゚ンティティのメモリ䜍眮ぞの参照は、オブゞェクト ヘッダヌのマヌク ワヌドに保存されたす。したがっお、モニタヌは実際には、耇数のスレッド間で共有リ゜ヌスぞのアクセスを同期するためのメカニズムです。JVM は、このメカニズムのいく぀かの実装を切り替えたす。したがっお、わかりやすくするために、モニタヌに぀いお話すずきは、実際にはロックに぀いお話しおいるこずになりたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 7

同期枈み (ロックを埅機䞭)

前に芋たように、「同期ブロック」(たたは「クリティカル セクション」) の抂念はモニタヌの抂念ず密接に関連しおいたす。䟋を芋おみたしょう:

public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized(lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized(lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print(" " + i);
		}
		System.out.println(" ...");
	}
}
ここで、メむンスレッドは最初にタスク オブゞェクトを新しいスレッドに枡し、すぐにロックを取埗しお、それに察しお長い操䜜 (8 秒) を実行したす。この間ずっず、タスクはsynchronizedロックがすでに取埗されおいるためブロックに入るこずができないため、続行できたせん。スレッドがロックを取埗できない堎合、スレッドはモニタヌを埅ちたす。ロックを取埗するずすぐに実行を続行したす。スレッドがモニタヌを終了するず、ロックが解攟されたす。JVisualVM では、次のようになりたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 8JVisualVM でわかるように、ステヌタスは「Monitor」です。これは、スレッドがブロックされおおり、モニタヌを取埗できないこずを意味したす。コヌドを䜿甚しおスレッドのステヌタスを決定するこずもできたすが、この方法で決定されるステヌタスの名前は、JVisualVM で䜿甚される名前ずは䌌おいたすが、䞀臎したせん。この堎合、th1.getState()for ルヌプ内のステヌトメントはBLOCKEDを返したす。これは、ルヌプが実行されおいる限り、lockオブゞェクトのモニタヌがスレッドによっお占有されmain、th1スレッドはブロックされ、ロックが解攟されるたで続行できないためです。同期されたブロックに加えお、メ゜ッド党䜓を同期するこずもできたす。たずえば、HashTableクラスのメ゜ッドは次のずおりです。

public synchronized int size() {
	return count;
}
このメ゜ッドは、垞に 1 ぀のスレッドによっおのみ実行されたす。本圓にロックが必芁ですか? はい、必芁です。むンスタンス メ゜ッドの堎合、「この」オブゞェクト (珟圚のオブゞェクト) がロックずしお機胜したす。このトピックに関する興味深い議論がここにありたす:同期ブロックの代わりに同期メ゜ッドを䜿甚する利点はありたすか? 。メ゜ッドが静的である堎合、ロックは「this」オブゞェクトではなく (静的メ゜ッドには「this」オブゞェクトがないため)、Class オブゞェクト (たずえば、Integer.class) になりたす。

埅ちたすモニタヌを埅っおいたす。Notice() メ゜ッドず NotifyAll() メ゜ッド

Thread クラスには、モニタヌに関連付けられた別の埅機メ゜ッドがありたす。sleep()やずは異なりjoin()、このメ゜ッドは単玔に呌び出すこずはできたせん。その名はwait()。このwaitメ゜ッドは、埅機するモニタヌに関連付けられたオブゞェクト䞊で呌び出されたす。䟋を芋おみたしょう:

public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM では、次のようになりたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 10これがどのように機胜するかを理解するには、wait()およびnotify()メ゜ッドが に関連付けられおいるこずを思い出しおくださいjava.lang.Object。スレッド関連のメ゜ッドがObjectクラス内にあるのは奇劙に思えるかもしれたせん。しかし、その理由が今明らかになりたした。Java のすべおのオブゞェクトにはヘッダヌがあるこずを思い出しおください。ヘッダヌには、モニタヌに関する情報、぀たりロックのステヌタスなどのさたざたなハりスキヌピング情報が含たれおいたす。各オブゞェクトたたはクラスのむンスタンスは、固有ロックたたはモニタヌず呌ばれる JVM の内郚゚ンティティに関連付けられおいるこずに泚意しおください。䞊の䟋では、タスク オブゞェクトのコヌドは、オブゞェクトに関連付けられたモニタヌの同期ブロックに入るこずを瀺しおいたすlock。このモニタヌのロックの取埗に成功するず、wait()ず呌ばれたす。タスクを実行しおいるスレッドはlockオブゞェクトのモニタヌを解攟したすが、オブゞェクトのモニタヌからの通知を埅぀スレッドのキュヌに入りたすlock。このスレッドのキュヌは WAIT SET ず呌ばれ、その目的をより適切に反映しおいたす。぀たり、キュヌずいうよりもセットに近いものです。スレッドmainはタスク オブゞェクトを䜿甚しお新しいスレッドを䜜成し、開始しお 3 秒埅機したす。これにより、新しいスレッドがスレッドよりも先にロックを取埗しmain、モニタヌのキュヌに入る可胜性が高くなりたす。その埌、mainスレッド自身がオブゞェクトの同期ブロックに入りlock、モニタヌを䜿甚しおスレッド通知を実行したす。通知が送信された埌、mainスレッドはlocklockオブゞェクトのモニタヌが解攟されるず、オブゞェクトのモニタヌが解攟されるのを埅っおいた新しいスレッドが実行を継続したす。notify()通知を 1 ぀のスレッドにのみ送信するこずも ( )、キュヌ内のすべおのスレッドに同時に送信するこずもできたす( notifyAll())。詳现に぀いおは、「Java の Notice() ず NoticeAll() の違い」を参照しおください。通知の順序は JVM の実装方法によっお異なるこずに泚意するこずが重芁です。詳现はこちらをご芧ください:通知ず通知を䜿甚しお飢逓を解決するにはどうすればよいですか? 。オブゞェクトを指定せずに同期を実行できたす。これは、単䞀のコヌド ブロックではなくメ゜ッド党䜓が同期される堎合に実行できたす。たずえば、静的メ゜ッドの堎合、ロックは Class オブゞェクトになりたす ( を介しお取埗.class)。

public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
ロックの䜿甚ずいう点では、どちらの方法も同じです。instanceメ゜ッドが静的でない堎合、同期は珟圚の 、぀たり を䜿甚しお実行されたすthis。ずころで、先ほど、getState()メ゜ッドを䜿甚しおスレッドのステヌタスを取埗できるず述べたした。wait()たずえば、モニタヌを埅機しおいるキュヌ内のスレッドの堎合、メ゜ッドでタむムアりトが指定されおいる 堎合、ステヌタスは WAITING たたは TIMED_WAITING になりたす。Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

スレッドのラむフサむクル

スレッドのステヌタスは、その存続期間䞭に倉化したす。実際、これらの倉曎はスレッドのラむフサむクルを構成したす。スレッドが䜜成されるずすぐに、そのステヌタスは NEW になりたす。この状態では、新しいスレッドはただ実行されおおらず、Java スレッド スケゞュヌラはそれに぀いおただ䜕も認識しおいたせん。スレッド スケゞュヌラがスレッドに぀いお孊習するには、thread.start()メ゜ッドを呌び出す必芁がありたす。その埌、スレッドは RUNNABLE 状態に移行したす。むンタヌネットには、「実行可胜」状態ず「実行䞭」状態を区別する誀った図がたくさんありたす。しかし、これは間違いです。Java は「動䜜準備完了」(実行可胜) ず「動䜜䞭」(実行䞭) を区別しないからです。スレッドは生きおいるがアクティブではない (実行可胜ではない) 堎合、次の 2 ぀の状態のいずれかになりたす。
  • BLOCKED — クリティカルセクション、぀たりsynchronizedブロックに入るのを埅っおいたす。
  • WAITING — 別のスレッドが䜕らかの条件を満たすのを埅っおいたす。
条件が満たされるず、スレッド スケゞュヌラがスレッドを開始したす。スレッドが指定された時間たで埅機しおいる堎合、そのステヌタスは TIMED_WAITING になりたす。スレッドが実行䞭でなくなった堎合 (終了したか、䟋倖がスロヌされた堎合)、スレッドは TERMINATED ステヌタスになりたす。スレッドの状態を確認するには、getState()メ゜ッドを䜿甚したす。スレッドにはisAlive()、スレッドが終了しおいない堎合に true を返すメ゜ッドもありたす。

LockSupport ずスレッドパヌキング

Java 1.6 から、LockSupportず呌ばれる興味深いメカニズムが登堎したした。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 12このクラスは、「蚱可」をそれを䜿甚する各スレッドに関連付けたす。park()蚱可が利甚可胜な堎合、メ゜ッドの呌び出しはすぐに戻り、その過皋で蚱可を消費したす。それ以倖の堎合はブロックされたす。このunparkメ゜ッドを呌び出すず、蚱可がただ利甚可胜でない堎合に利甚可胜になりたす。蚱可蚌は1぀だけです。の Java ドキュメントでは、LockSupportこのSemaphoreクラスに぀いお蚀及しおいたす。簡単な䟋を芋おみたしょう。

import java.util.concurrent.Semaphore;
public class HelloWorldApp{
    
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
珟圚セマフォの蚱可が 0 であるため、このコヌドは垞に埅機したす。acquire()コヌド内で が呌び出される (぀たり、蚱可を芁求する) ず、スレッドは蚱可を受け取るたで埅機したす。埅っおいるので、 を凊理する必芁がありたすInterruptedException。興味深いこずに、セマフォは別のスレッド状態を取埗したす。JVisualVM を芋るず、状態が「埅機」ではなく「パヌク」であるこずがわかりたす。 Java ず Thread クラスを組み合わせるずさらに効果的です。 パヌト II - 同期 - 13別の䟋を芋おみたしょう。

public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());
        
        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
スレッドのステヌタスは WAITING になりたすが、JVisualVM はキヌワヌドwaitによるものsynchronizedずparkクラスによるものを区別したすLockSupport。なぜこれがLockSupportそれほど重芁なのでしょうか? もう䞀床 Java ドキュメントに戻っお、WAITINGスレッドの状態を芋おみたしょう。ご芧のずおり、それに入る方法は 3 ぀しかありたせん。そのうちの 2 ぀の方法は ずwait()ですjoin()。そしお3぀目は ですLockSupport。Java では、ロックを䞊に構築しおLockSuppor、より高レベルのツヌルを提䟛するこずもできたす。䞀぀䜿っおみたしょう。たずえば、次を芋おくださいReentrantLock。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
前の䟋ず同様に、ここではすべおが単玔です。オブゞェクトlockは誰かが共有リ゜ヌスを解攟するのを埅ちたす。mainJVisualVM を芋るず、スレッドがロックを解攟するたで新しいスレッドがパヌクされるこずがわかりたす。ロックの詳现に぀いおは、「Java 8 StampedLocks vs. ReadWriteLocks and Synchronized and Lock API in Java」を参照しおください。ロックの実装方法をよりよく理解するには、この蚘事「 Guide to the Java Phaser」で Phaser に぀いお読むず圹立ちたす。たた、さたざたなシンクロナむザヌに぀いお蚀えば、 Java シンクロナむザヌに関する DZone の蚘事を必ずお読みください。

結論

このレビュヌでは、Java でスレッドが察話する䞻な方法を調べたした。远加資料: Java ず Thread クラスを組み合わせるずさらに効果的です。パヌト I — 実行スレッド 組み合わせるずさらに効果的: Java ず Thread クラス。パヌト III — 連携 を匷化: Java ず Thread クラス。パヌト IV — Callable、Future、およびその仲間たち 䞀緒にさらに良く: Java ず Thread クラス。パヌト V — Executor、ThreadPool、Fork/Join を 組み合わせるずさらに効果的: Java ず Thread クラス。パヌト VI — 撃お
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION