GOLD/リーダー/ボールド歴2年2020/1/24TweetPocketJavaのスレッド(thread)とは、プログラム上で複数の処理を同時に動かす仕組みです。スレッドをJavaで使うためのクラスjava.lang.Threadを指す言葉でもあります。多くの作業は人が手分けすれば早く終わります。違う作業であっても、それぞれの作業へ人を割り当てれば同時に行えます。一人での作業より効率的です。それと同じで、プログラムでも処理を複数同時に動かせば、短い時間で効率よく処理できます。その仕組みがスレッドです。スレッドは、今ではごく当たり前に使われているのです。この記事では、Javaでのスレッドの考え方・使い方の基本から、スレッドを使う上で気を付けたいこと、スレッドに関する話題について、ポイントを絞って初心者向けに説明します。13時点の言語仕様・APIに基づいています。13の環境で動作確認しています。1.5~)4.まとめ1.スレッドの基本的な考え方・使い方ここでは、スレッドのことを軽く学んで、Javaのスレッドを体験してみましょう。Javaでは、スレッドを作って動かすことは、とても簡単なことなのです。やるべきことは、スレッドで動かしたい処理を普通のメソッドと同じように作って、java.lang.Threadに「この処理を動かしてください」と頼むだけなのです。1-1.スレッドの考え方スレッド(thread)とは、プログラムが処理を実行する単位をプログラマの必要に応じて増やせるものです。スレッドは、主にコンピュータの処理能力(特に、マルチコアやマルチプロセッサ)を最大限に生かすために使われます。プログラムでのスレッドの使い道は、例えば以下のものです。違うことを同時に行う:あるスレッドでネットワーク通信をしつつ、別のスレッドではユーザが行った画面操作への処理ができます処理にかかる時間を短くする:大きな問題を同時に処理できるより小さな問題に分割し、小さな問題をスレッドで同時に処理して、全体時間を短縮します単位時間当たりの処理量を増やす:コンピュータにスレッドを同時に動かせるだけの性能があれば、単位時間当たりに処理できる量を増やせます身の回りのソフトウェアを思い浮かべると、いくつかの処理を同時に動かしていそうなものがあるでしょう。WEBブラウザでも何でも、今ではスレッドを使って動くのが当たり前なのです。1-1-1.スレッドをコンビニで例えるとプログラム以外でスレッドを考えてみるなら、コンビニエンスストアを思い浮かべましょう。お昼時などでお客さんでごった返しているコンビニがいいですね。コンビニのレジが1つだけなら、すぐ精算の行列ができます。レジが複数あれば、精算を同時に複数できて、行列は短くなります。それに店員が複数人いれば、違う仕事が同時にできますよね(レジ打ちや品出し、清掃など)。コンビニがプログラムだとすれば、店員がスレッドです。共通しているのは、何かの仕事や処理をする単位だということです。店員はすぐには増やせませんが、プログラムではスレッドを必要に応じてすぐに作り動かせるのです。1-2.Threadクラスを継承して使ってみるでは、Javaで実際にスレッドを使ってみましょう。スレッドを使う方法の一つは、java.lang.Threadクラスを継承したクラスを作り、インスタンス化して動かすことです。1-2-1.スレッドを1つ作って動かしてみるThreadを継承したクラスでは、スレッドで実行したい処理をrunメソッドの中に書きます。普通にメソッドへ書けることであれば、例えばファイル読み込み、ネットワーク通信、数値計算など、大抵のことが出来ます。{System.out.println("スレッドで動いてまーす");}}スレッドを動かすには、このThreadを継承したクラスのインスタンスを生成し、startメソッドを呼び出します。これだけで、新しいスレッドが生成され、そのスレッドでrunメソッドへ記述した処理を動かせられます。ThreadSample();t.start();}}さっそくThreadExecutorを動かしてみると、以下のような出力になるでしょう。おめでとうございます。見事、スレッドを使えました。実行結果1スレッドで動いてまーすここで、プログラム上ではThreadSampleのrunを呼び出してもいないのに、文字が表示されました。スレッドがrunメソッドを自動で呼び出してくれた、ということですね。重要なポイントは、プログラマが自分でrunを呼び出すのではないということです。スレッドがいつrunを呼び出すかは指示できず、スレッドの準備が終わったタイミングで呼び出されます。スレッドを使う上では、このようにプログラマが制御できないことが多くあるのです。いわゆるシングルスレッドなプログラムとは違う箇所が出てきます。1-2-2.いくつかのスレッドを同時に動かしてみる先ほどのプログラムでは、スレッドを1つ作って動かしただけでした。スレッドは複数のスレッドを同時に動かしてこそ、ですよね。次はスレッドを3つ作り、同時に動かしてみましょう。さあ、いよいよマルチスレッドプログラミングっぽくなってきました。"で動いてまーす");}}出力メッセージが同じだと、3つの別々のスレッドが同時に動いたのかが分からないので、スレッドごとに割り当てられるユニークな名前も一緒に出力してみます。runの中で呼んでいるgetNameメソッドは、スレッドの名前を取得できるThreadクラスのメソッドです。ThreadSampleはThreadのサブクラスですから、runからgetNameを呼び出せるのです。ThreadSample();t1.start();t2.start();t3.start();}}では、またThreadExecutorを実行してみましょう。実行結果123スレッドThread-0で動いてまーすスレッドThread-2で動いてまーすスレッドThread-1で動いてまーすと、このように違うスレッド名が3行出力されました。プログラムは同じでも、実行結果はスレッドごとに違います。これが、それぞれの処理を動かしたスレッドが別だということです。このプログラムが動く時のイメージは、以下のような感じです。今の処理から3つのスレッドが分岐して、それぞれが独立して動いているのです。1-3.ThreadクラスとRunnableインターフェイスで使ってみるスレッドを使う方法のもう一つは、スレッドで動かしたい処理をjava.lang.Runnableの実装クラスとして作り、そのRunnableをThreadクラスのインスタンスに動かしてもらうことです。実際には、こちらが使われます。Runnableとは「実行(run)できる(able)もの」という意味のインターフェイスで、抽象メソッドは戻り値void、引数なしのrunだけです。Runnableはスレッドに限らず、Javaで何か動かしたい処理を表現・実装するのにもよく使われます。Runnableを使うと、スレッドそのものと、スレッドで動かしたい処理をプログラム上で明確に分けられます。このような役割分担は、プログラムの見通しの良さにもつながる考え方です。1-3-1.1つのスレッドから呼び出してみるさて、実際のプログラムを見てみましょう。RunnableSampleはRunnableを実装しています。やっていることはThreadを継承した場合と同じで、runメソッドの実装があるだけです。"で動いてまーす");}}今回は、runメソッドの中で「Thread.currentThread().getName()」でスレッドの名前を取得しています。RunnableはThreadではないので、Thread.getName()を直接使えないからです。RunnableSampleのインスタンスを生成し、Runnableを引数に取るThreadのコンストラクタへ渡してThreadのインスタンスを生成します。その後、Threadのstartメソッドを呼び出します。Runnableをスレッドに渡してインスタンスを生成するt.start();}}結果は、例えば以下となります。実行結果1スレッドThread-0で動いてまーすRunnableSample.runをプログラム上からは呼び出していませんが、実際には呼び出されています。これはThreadを継承した時と同じように、スレッドからrunメソッドが呼び出されたのです。1-3-2.いくつかのスレッドから呼び出してみるでは次に、いくつかのスレッドからRunnableを呼び出してみましょう。RunnableSampleのインスタンスを生成し、ThreadのRunnableを引数に取るコンストラクタに渡して、Threadのインスタンスを3つ作りました。その後、スレッド3つを開始させています。Thread(r);t1.start();t2.start();t3.start();}}結果は以下です。ここで、RunnableSample.runが動いた時のスレッド名はすべて違います。RunnableSampleのインスタンスは1つだけです。これは何を意味するのでしょうか。実行結果123スレッドThread-2で動いてまーすスレッドThread-1で動いてまーすスレッドThread-0で動いてまーすこれは、3つのスレッドが動いた結果、それぞれのスレッドからRunnableSample.runが3回呼び出され、結果として3行が出力されたということです。メソッドは、複数のスレッドから同時に呼び出されることがある、ということです。これはインスタンスメソッドに限らず、クラスメソッドでも同じことです。言えば、インスタンスやクラスのフィールドも、複数のスレッドから同時に読み書きされます。これらはプログラミング上の実に厄介な課題なのですが、詳細は後からお伝えします。2.Threadのメソッドの使い方Threadには便利なメソッドが数多くあります。その中でも、比較的よくプログラム上で出てくるものの使い方を、簡単にご紹介します。2-1.スレッドの識別子・名前を取得するThread.getId/getNameスレッドへは、スレッドの識別のための数字(識別子)をJava仮想マシンが与えます。それ以外にも、Java仮想マシンが自動で名前も付けます(プログラマが名前を付けることもできます)。そのスレッドの識別子と名前を取得するためのメソッドが、Thread.getIdとgetNameです。"です");}}実行結果12t1の識別子は12、名前はThread-0ですt2の識別子は13、名前はThread-1ですプログラム中では、今の処理がどのスレッドで動いているのかを知りたい時があります。その時にこれらのメソッドを使えば、どのスレッドで動いているか分かります。2-2.今のスレッドを取得するThread.currentThreadThreadを継承したクラスなら、自分自身がスレッドとして何者かは簡単に分かります。自分自身がスレッドなので、自分自身のgetId/getNameを直接呼び出せばいいからです。"です");}}一方、Runnableやスレッドから呼び出されたメソッドでは、今どのスレッドで動いているのかはすぐには分かりません。今のThreadを何らかの方法で持ってこなければ、Thread.getId/getNameを呼び出せないからです。そんな時にThread.currentThreadを呼び出せば、今の処理を動かしているThreadのインスタンスを取得できます。そこからgetId/getNameを呼び出せば、どのスレッドかが分かります。Thread(r);t1.start();t2.start();}}実行結果12スレッドの識別子は12、名前はThread-0ですスレッドの識別子は13、名前はThread-1ですRunnableSampleのインスタンスは一つですが、runメソッドの呼び出し元になっているスレッドが違うので、それぞれの出力で違う識別子・名前になっています。2-3.今のスレッドを一時停止するThread.sleep時には、スレッドの処理を一時停止したい場合があります。何らかの処理待ちをしたい場合などです。いう時にはThread.sleepを呼び出します。Thread.sleepを呼び出すと、今実行中のスレッドを引数で指定した秒数停止できます。nanos))では、ナノ秒での指定もできます。ThreadSample();t.start();}}停止するのはThread.sleepを呼び出したスレッドだけです。それ以外のスレッドは実行され続けます。それに、任意のスレッドの実行を、別のスレッドから直接停止させられもしません。これは結構分かりづらいポイントです。sleepの動きが意図どおりにならないなら、どのスレッドでsleepしているかを、getIdやgetNameで調べてみましょう。停止しているスレッドへ、後述する割り込みが起きた場合は、途中で停止状態が解除されることもあり得ます。その場合は、catchしている例外InterruptedExceptionが発生します。Thread.sleepについては個別記事もありますので、参考にしていただければと思います。関連記事Javaでプログラムを一時停止、Thread.sleepの使い方と仕組みを解説2019/02/212-4.スレッドの処理が終わるのを待つThread.join特定のスレッドの処理が終わるのを待つには、そのスレッドのThread.joinを呼び出します。Thread.sleepと同じように、自分自身のスレッドの処理は停止します。それが再び動き出すきっかけは、Thread.joinが呼び出されたスレッドの処理の終了です。10秒後にこれが表示される2-5.スレッドに割り込むThread.interrupt処理中のスレッドへ、処理の中断を伝えたい場合があります。その際は、スレッドの割り込みという機能を使うと、すぐに伝えられます。2-5-1.Thread.interruptで割り込むスレッドへの割り込みは、対象のスレッドのThread.interruptを呼び出すことで行えます。スレッドがThread.sleepなどで待ちの状態にある場合などでは、割り込みされたスレッドでInterruptedExceptionが直ちに発生しますので、割り込みをされたことが分かります。←10秒待たずに表示されるなお、割り込みをInterruptedExceptionで検知できるケースは限定されています。具体的にはObject.wait、Thread.join、Thread.sleepでスレッドが待ちの状態にある場合です。2-5-2.Thread.interrupted/isInterruptedで割り込みを検出するスレッドが前述した状態(sleep等)ではない時に割り込みされたかを知るには、Thread.interruptedやThread.isInterruptedを呼び出します。今のスレッドが割り込みされたか調べるには、Thread.interruptedを使います。特定のスレッドが割り込みされたか調べるには、そのスレッドのThread.isInterruptedを使います。それぞれのメソッドでは、割り込みされた直後の呼び出しではtrueを戻し、以後は再び割り込みされなければfalseを戻します。割り込みがあったか分かるのは最初の一回だけということです。{}System.out.println("スレッドに割り込みます");t1.interrupt();t2.interrupt();System.out.println("スレッドに割り込みました");}}以下のとおり、ループをしている最中に割り込んでも処理は中断されません。ループ後にThread.isInterruptedまたはThread.interruptedを呼び出すと、割り込みがあったかが分かります。←各スレッドのループ終了後に以下が表示される別のスレッドから割り込まれています2ループが終わりました1別のスレッドから割り込まれています12-6.例外を処理するThread.UncaughtExceptionHandlerThread.UncaughtExceptionHandlerは、Thread.runやRunnable.runで発生した例外を補足するために使います。2-6-1.スレッド内で発生した例外の扱いメソッド宣言から明らかなように、Thread.runやRunnable.runは、チェック例外をthrowできません。処理中で発生したチェック例外は、runの中でtry-catch文ですべて処理するか、throw句への記載が不要なRuntimeExceptionとしてthrowします。RuntimeExceptionをthrowした場合は、例外がthrowされるとそのスレッドの実行全体が止まります。その上で、何が起きたのかもスレッドの呼び出し元からは簡単には分かりません。ThreadSample.run(ThreadSample.java:6)このプログラムを動かした時は、runメソッドから例外がthrowされても、mainメソッドにあるcatch句には入りません。スレッドで発生した例外はJava仮想マシンが受け取って処理しますが、プログラム上でスレッドで発生した例外を知り、何かの処理をさせるにはひと工夫必要です。2-6-2.Thread.UncaughtExceptionHandlerで例外の発生を知るThread.UncaughtExceptionHandlerを使うと、スレッドで発生した例外へのフォロー処理を設定できます。例外発生時のスレッドでの処理を再開できるわけではありませんが、必要な後処理(ログ出力など)を行う機会が得られます。"からRuntimeExceptionをthrowします");}}}以下のように、Thread.UncaughtExceptionHandlerのインスタンスを生成し、Thread.setUncaughtExceptionHandlerメソッドの引数として、Threadに引き渡します。Thread.UncaughtExceptionHandlerのuncaughtExceptionメソッドが、例外発生時に呼び出されます。uncaughtExceptionメソッドの引数へは、例外が発生したスレッドと例外が渡されます。jp.engineer_club.thread.ThreadSample.run(ThreadSample.java:6)3.スレッド関連のトピックこの章では、スレッドに関連するトピックをお伝えします。3-1.スレッドでの処理結果の受け取りスレッドでの処理結果をスレッドの外部から受け取りたい時があります。Thread.runとRunnable.runのどちらも、メソッドの戻り値はvoidなので、結果を呼び出し元へ直接は戻せません。スレッドでの処理結果の受け取り方には、大まかには三種類あります。以下では、それぞれのやり方をサンプルを交えて説明します。・ポーリング:Thread/Runnableに処理が終わったか問い合わせ、終わったら結果を得るメソッドを呼ぶコールバック:Thread/Runnableに処理が終わった後に呼び出すオブジェクトを渡し、終わったら所定のメソッドを呼んでもらうデータ共有用オブジェクト:処理結果を受け取るオブジェクトを、呼び出し元と先とで共有する3-1-1.ポーリングの例ポーリング(polling)とは、プログラミングでは定期的に問い合わせをする処理方法のことです。シンプルかつ直感的な処理方式です。ポーリングでは、スレッドでの処理が終わったかを、スレッド自身へ定期的に問い合わせます。スレッドが終わったと回答したなら、そこで結果を受け取るのです。”です”);}}実行結果1スレッドでの処理結果は55ですプログラムを簡単にするために、Thread.sleepしながらループしています。実際には、このsleepの箇所で別の処理をします。すると、コンピュータの処理時間を効率的に使えます。欠点は、呼び出し元が定期的に状態を聞かなければならないことです。「あとはよろしく~」と、仕事を丸投げできず、終わりまで面倒を見なければなりません。3-1-2.コールバックの例コールバック(callback)とは、一般的には「(電話を)かけ直す・かけ返す、呼び直す」という意味です。プログラミングでのコールバックは、呼び出し元が処理を事前に指定して、終わった時に呼び出し先から呼び返してもらうやり方のことです。以下は、スレッドでの処理結果をコールバックを使って受け取る例です。ThreadSample();t.setCallback(callback);t.start();}}実行結果1スレッドでの処理結果は55ですCallbackインターフェイスを実装したクラス(今回は匿名クラスです)が、スレッドでの処理が終わった後に呼ばれる処理です。処理結果は、finishedメソッドの引数で渡されますので、それを使って何かをします。スレッドを作る側のプログラムは、ポーリングの例と比べるとずいぶんすっきりしましたね。これは、ポーリングで必要だった「スレッドの処理待ちをする処理」がないからです。要注目は、終わった後の処理がCallbackインターフェイスで抽象化されたことです。ThreadSampleは、処理の実装が何であれfinishedメソッドを呼び出すだけなので、汎用性が増しています。呼び出し元と呼び出し先とで、何かしらのオブジェクトを共有するやり方があります。そのオブジェクトで処理結果や、処理のパラメータを受け渡しできます。スレッドはプログラム上で動くものですが、一つのプログラムの中でなら、メモリを経由すればスレッド間でデータを共有できるのです。プログラムを越えてデータのやりとりをしたい場合は、プログラムの外にあるファイルやデータベース、ネットワーク通信などを使わなければなりません。以下は、データ共有オブジェクトを使ってみた例です。"です");}}実行結果1スレッドでの処理結果は55ですデータを持つSharedDataクラスのインスタンスを、呼び出し元と先で共有していますね。プログラムの構造が、ポーリングと似ているのはたまたまです。この例では、2つのスレッド間でデータを共有しましたが、もちろんスレッド数は2つだけに限定されず、必要なだけ広げられます。それがこのやり方のメリットです。複数のスレッド間でのデータ共有は危険でもあります。プログラムが複雑になりますし、後述する同期化・排他ロックの問題がとても表面化しやすいからです。publicなクラス変数を共有データとして使えもしますが、そうすべきではありません。いわゆるグローバル変数と同じことですから、オブジェクト指向で対処してきた様々な問題が再発しかねません。3-1-4.コールバックの使い過ぎには要注意コールバックは、スレッド関連以外でも実用プログラムでは多用されます。JavaScriptなどでのHTMLアプリや、Android/iOSでのスマホアプリのようなGUIアプリではごく普通に使いますので、慣れておくべきです。便利だからとコールバックを使いまくると、プログラムの構造や処理の呼び出し順序が直感的に分かりづらくなり、大きな問題となります。hell)」という言葉があるくらいなのです。Extensions)などのフレームワークが活用されたり、Java以外でもプログラミング言語の機能強化が続いていたりします。素直にポーリングする方が簡単かつプログラムが読みやすくなる場合も多いので、適材適所でやり方を選びましょう。3-2.【参考】mainメソッドはmainスレッドが動かすJavaのプログラムが動く時には、必ず何かのスレッドがあり、動いています。言い方を変えれば、Javaのプログラムは必ず何かのスレッドが動かしている、ということです。以下のプログラムを動かしてみましょう。mainメソッドを動かしているスレッドの名前を出力しています。"で動いてまーす");}}動かした結果は、以下となりました。このmainメソッドは、“main”という名前のスレッドが動かしているようですね。実行結果1スレッドmainで動いてまーすでも、このプログラム中ではThreadまたはThreadのサブクラスのインスタンスを作っていません。スレッドの名前はあるので、誰かがmainスレッドを作って動かしているのです。このmainスレッドを作って動かしているのは、Java仮想マシンです。Javaが動く時にはJava仮想マシンがmainスレッドを自分自身で作り、そのmainスレッドでmainメソッドを動かします。皆さんは、以下のエラーを見たことがあるかもしれません。これはmainメソッドがないクラスを実行しようとした時のものです。このメッセージの“main”こそが、mainメソッドのことです。"main"1-2と1-3では、スレッドを3つ作って動かしました。これは、より正確に言えばmainスレッドからスレッドを3つ作って動かしたのです。プログラム上では一時的にスレッドが4つあったことになります。ロックの考え方スレッドを使う上でほぼセットになるのが、同期化や排他ロックと呼ばれる考え方です。これらは、スレッドにより「処理が同時に動く」ことを防ぐ仕組みです。同期化や排他ロックをきちんと使いこなさなければならない局面が、スレッドを使う上での腕の見せ所です。スレッドを使いこなすためにも、しっかり学んでおいてください。3-3-1.1を10000回足しても10000にはならない(ことがある)以下のプログラムは、10個のスレッドがそれぞれ1,000回、int型の変数を+1し続けます。10×1,000は当然10,000ですよね。これを実行した場合に、結果が常に10,000になるとは限りません。int型の変数を+1し続けているだけなのに、です。"です";}}}実行結果123456789101回目:処理結果は8796です2回目:処理結果は9559です3回目:処理結果は8183です4回目:処理結果は9139です5回目:処理結果は9675です6回目:処理結果は9767です7回目:処理結果は9967です8回目:処理結果は10000です9回目:処理結果は10000です10回目:処理結果は10000ですさて、皆さんの環境での実行結果はいかがでしたでしょうか。単純な足し算をしているだけなのになかなか不思議ですが、これがスレッドを使ったプログラムの難しさの一側面です。3-3-2.同時に動くことはやっかいな問題を生むこうなる理由は、変数の数値を+1するだけでも、Java内部では以下のステップを経るからです。これらのステップが複数スレッドで同時に動くと、結果が意図したとおりにならないのです。スレッドがメモリ上の変数countの現在値を読み、スレッド内に一時的に持つスレッド内で現在値を+1する+1した値を、変数countがあるメモリにスレッドが書き戻す例えば、countの現在の値が10だとします。2つのスレッドがcountを「同時に」読み出したなら、2つのスレッドが10という値を一時的に持ちます。10を+1すれば11ですから、2つのスレッドがそれぞれ書き込みをすれば、countの値は12ではなく11になります。専門用語を使うなら、変数countはヒープ領域にあるので、スレッドがその値を使った計算を行うため、スレッドごとのスタック領域へ一時的に値をコピーするために起こる現象です。変数の値を+1するだけでもこれですから、もっとやっかいな問題が起きそうです。実際スレッド関連では、いくらやっても再現できなかったり、原因がさっぱり分からず解決の糸口すらつかめない問題もよくあります。3-3-3.同期化で同時に動くことを防ぐこの例で、常に結果を10,000にしたければ、複数のスレッドへ変数を同時に読み書きさせてはいけません。同時の変数の読み書き、メソッド呼び出しを防ぐ機能が、同期化や排他ロックと一般に呼ばれるものです。今回の例では、RunnableSampleのメソッドrunを、複数のスレッドから同時に動かせなくすれば良さそうです。そのために、synchronizedというキーワードをメソッドの前につけてみます。count;}}すると、結果は常に10,000です。synchronizedというキーワードにより、複数のスレッドからrunという「変数を+1する処理」が同時実行されなくなるからです。この状態で同時にメソッド呼び出しが行われたら、処理を実行する権利を得たスレッド以外は、メソッドの終了待ちをします。メソッドが終わったら、またどれかのスレッドが実行権を得て実行し始めます。Javaでの同期化や排他ロックの方法は複数あります。キーワードを挙げておきますので、興味のある方は調べてみてください。それぞれ一長一短がありますので、使い分けが必要ですよ。Utilities、特にその中に含まれるExecutorフレームワークは、スレッドをもっと簡単に使うためのものです。Javaも13となった今では、ここまで説明してきたThreadクラスはあまり使わず、Executorフレームワークを使うのが普通です。スレッドの考え方は同じですし、Threadクラスも同じように使えます。Executorフレームワークではスレッドを使う上で不便だった制約や問題点が多く解消されています。Executorフレームワークで解消された制約や問題点は、例えば、Threadそのものをプログラマが管理しないといけなかったり、スレッドでの実行結果を直接得られないなどです。3-4-1.Executorフレームワークの例(Executors/ExecutorService)Executorフレームワークを使うと、以下のような感じになります。Runnableでスレッドで動かす処理を作るのは同じです。Threadがプログラム中にありませんし、見たことのないクラスもいくつかあります。"です");}}Threadの代わりに使うのが、ExecutorsやExecutorServiceです。これらのクラスに面倒なスレッドの管理をすべて任せて、使う側が「スレッドをこんな感じで使いたい」と、Executors/ExecutorServiceへ伝えます。Executorsは、使う側の要求に応じたExecutorServiceを作ります。この例では、1つのスレッドを管理するExecutorServiceを要求しています。ExecutorServiceへ動かすRunnableを渡し、実行させています。pool)と呼ばれる機能も簡単に使えます。このように、スレッドを活用したプログラムを実に簡単に、手軽に作れるのです。3-4-2.Executorフレームワークの例(Callable)Executorフレームワークでは、動かす処理を表現するインターフェイスとしてjava.util.concurrent.Callableが追加されました。Callableを使うと、スレッドでの実行結果をスレッドの外から直接得られるのです。{}service.shutdown();}}Futureを間に挟むので少々ややこしいですが、スレッドでの処理結果をFuture.getで得られるので、先述のポーリングやコールバックが不要となり、より直感的にプログラムを作れます。Callableを使うとスレッドで動かす処理からチェック例外をthrowでき、スレッドを呼び出した側からもスレッドからthrowされた例外を取得できたりもするのです。Utilitiesでは、ExecutorServiceやCallable以外にも、以下のような便利なクラス群が追加されました。値への操作が同期化されたAtomicInteger、AtomicBooleanなど排他ロックを簡単に行うためのReentrantLock同期化の処理パフォーマンスが改善されたConcurrentArrayListなどスレッド間での待ち合わせができるCyclicBarrier、Semaphore、Phaserなど定期的な処理をスレッドで実行できるScheduledExecutorServiceスレッド実行後の後続処理を指定できるCompletableFutureスレッドを使って処理を効率化するためのForkJoinPool/ForkJoinTaskなどこれらのクラスでスレッドのユースケースは全部実現できる、と言っても過言ではありません。これらのクラスを学び、スレッドを活用したプログラムを作れるようになってください。4.まとめスレッドとは処理を実行する単位です。JavaではThreadクラスでスレッドの機能を使います。スレッドとセットなのは、Runnableという「実行する処理」を表現するインターフェイスです。スレッドの難しさは、メソッドやフィールドが複数スレッドから同時に呼び出されたりアクセスされる部分です。それを解決するために同期化や排他制御などのいろいろな手段が用意されています。現時点でスレッドを扱うには、Executorフレームワークを使うのが普通です。Threadをそのまま使う時の問題点が解消されます。Utilitiesには、さらに便利なクラスがあります。スレッドの理解は、Java仮想マシンがどうプログラムを動かしているのかの深い理解にもつながります。この記事をきっかけに、Javaの仕組みを理解する一歩を踏み出してみましょう。私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。『技術力』と『人間力』を高め定年まで働けるエンジニアを目指しませんか?私たちは「技術力」だけでなく「人間力」の向上をもって、エンジニアとしてだけでなくビジネスパーソンとして高い水準を目指し、社会や顧客に必要とされることで、関わる人々に感動を与える集団であろうと思っています。定年までIT業界で働くためのスキルが身につく「感動大学」と「技術勉強会」!「給与が上がらない」を解消する6ヶ月に1度の明確な「人事評価制度」!理想のエンジニア像に近づくためのよきアドバイザー「専任コーチ制度」!稼動確認の徹底により実現できる平均残業時間17時間の働きやすい環境!現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。英人この道一筋20年。なんでもござれなエンジニア。システムやデータベースの設計、ソースコードの品質には一家言あり。気持ちはまだまだ若いので、若い世代のエンジニアと一緒に成長していきたい。EngineerclubFacebookTwitterFeedlyコメントコメントをキャンセル次回のコメントで使用するためブラウザーに自分の名前、メールアドレス、サイトを保存する。プログラミング言語とインフラでのインスタンスって?2020.01.15IT業界ヘルプデスクに転職したい。