マルチスレッド プログラミング 入門 研修コースに参加してみた
今回参加したコースは マルチスレッドプログラミング入門 です。
このコースではマルチスレッドの仕組みを解説頂いた上で、実際にコードを書いて複数スレッドが動いていることを確認しました。また Java5 -> Java7 とバージョンアップするごとに追加されたマルチスレッドに関連する機能を試してみました。
が、ちょっとかじっただけの私にはマルチスレッドは難しすぎました。。今まではコードを読みながら、講師の解説などをコメントに書けたのですが、それが今回は追いつかず、今回は仕組みや機能中心のレポートになっています。
なお、コースのアンケート結果では、
- Q. コースの難易度の評価
- ちょうどよかった 81%
- Q. コースの参加満足度
- 平均 7.7点 / 10点満点 (中央値8.0点)
このような評価なので、想定している受講前提、Javaプログラミングの中級レベルの知識を持っている方には理解しやすい内容です!
では、どんな内容だったのか、レポートします !!
もくじ
想定している受講者
- Javaプログラミングの中級レベルの知識(講座ではEclipseを使用)
受講目標
- Javaを使用したマルチスレッド・プログラミングの基本を習得する
講師紹介
今回も「米山です」と言って今日の狙いを話してスタートしましたが、プログラミング関連コースで登壇の多い 米山 学さん です。
今日の狙い
- Javaでマルチスレッドを書いて動作確認しながら理解します
- JDK 9を使います
- APIをいくつかピックアップして紹介します
- ただしマルチスレッドは奥が深いので、コース受講したあとに深掘ってください
マルチスレッドプログラミングとは
- OSがアプリケーションに割り当てる実行単位がプロセス
- アプリケーションがプロセス内で複数動かせるものがスレッド
- 軽量プロセスとも呼ばれる
- Java以外でも扱う
- 今回はJavaを使ってみます
- もともとの意味
- 意図とか筋、脈略という意味
- 処理の流れを表すもの
- Javaではスレッドオブジェクトと呼ばれる
- スレッドではメモリを共有する
- 設計や実装が難しい
- 同時並行 “的” にプログラムを実行する
- 処理が切り替わっているだけなので、完全同時という訳ではない
- スレッドのスケジューリングはOSもしくはJVMに依存する
- つまり実行環境に依存する
- 実行される保証がない
- なぜ、それでもマルチスレッドをやりたいのか?
- ネットワークのサーバーアプリケーション
- フロントからのリクエストによるイベントドリブンで処理する場合
- Web上でお絵かきや画像編集などを行うGUIアプリケーションなど
- 同時に数百万リクエストなど大量のデータを短時間に処理する
- マルチコアのCPUを活用する
- 難しいところ
- 同期するところ
- スレッドの実行順序に依存する
- 再現性があったり無かったり // 99回は正常1回だけ異常というケースもある
- DBなどのリソースにアクセスできないということもある
そもそも論ですが、マルチスレッドはトランザクションが同時数十万~が発生するようなソフトウェア、ミドルウェアを開発するときに必要になるため、なかなか実際に書くケースは限られるということでした。(例えば、Apacheの中を書く、動画ストリーミングサーバーを書く、JVMを書くなど)
Javaにおけるマルチスレッドプログラミング
- 言語仕様としてマルチスレッドをサポートしている
- 基本的にはシングルスレッドで実行される
- ただ実際にはバックグラウンドで複数にマルチスレッドで動いている
- 例えばGCなど
- mainスレッド
- mainスレッドがmainメソッドを呼び出す
- スレッドをオブジェクトとして扱う -> スレッドオブジェクト
- 自分でスレッドを作って、実行する
java.lang.Runnable
を implements することが推奨されている- 継承を使わない
run
というインターフェイスを持っているrun
の中に処理を書く
- スレッドスケジューラーを実行するのは難しい
- 確実に実行されるプログラムを書く
- スレッドがすべて終了するまで実行は終了しない
2つ以上のスレッドを実行する
- 同じインスタンスを複数スレッドで使用するときは問題が発生する
- スレッドの競合
- 順序は保証されない
- データを更新すると同期化が必要になる
- スレッドセーフではない、と言われる
- ただ、必ずしも問題が100%起こるわけではない
- ここがややこしい点
- スレッドの競合
synchronized
をつけたメソッドはロックされて実行される- プロセス内で1つしか無い
実際に実行してみると、同じコードなのに出力結果は変わり、その挙動が制御しにくそうなことは実感できました。
意外と知らない Java 5 で導入された便利機能
マルチスレッドの基本的な仕組みを理解できたところで、ここからはバージョンアップのたびに、制御できることが増えているので、それをバージョン毎に解説です。
java.util.concurrent
パッケージが導入された- スレッドプール
- スレッドオブジェクトの生成・破棄は重い処理になる
- Webアプリケーションではお馴染みのDBアクセスをプールにする
- 並行コレクション
- 複数のデータをまとめるコレクションをパフォーマンス
- ArrayList や LinkedList、set map などをまとめる
- 並行コレクションを使うと、同期化しながら読み書きできる
- 並行コレクションは自分で書くのではなく使う
- アトミック変数
- トランザクションのような仕組みを変数に適用したもの
- カウンティング・セマフォ
- 同時アクセス数を制限できる
- スレッドプール
CopyOnWriteArrayList
- 並行コレクション
- ArrayListに読み取りと書き込みが別々のスレッドが行われた場合、writeの場合はコピーを作って、そこに書き込む
- 書き込み処理がアクセスしていないときに読みに行く
- 若干パフォーマンスが落ちるときがある
- とはいえ synchronized するよりマシ
CyclicBarrier
- 名前の通りバリアをはる
- 他のスレッドが来るまで待ち合わせする
- 循環的に再利用できる
- 処理速度が異なるスレッドを後でマージするような処理を書きたいときに使う
Java7の並行処理に関する新機能
Excecutorフレームワーク
- タスクの実行者と実行方法を分離できる
ExecutorService
interface が実行主体- utility クラスが提供されているので、それを利用する
- これで不足する場合のみ
implements
しましょう
- 自分で細かく制御できるようになった
java.util.concurrent.Executors
を使うTimeUnit.SECONDS
というJDK5で新たに導入された時間指定のクラス
- スレッドが自動的に終了しないので、
shutdown
という終了のメソッドを使う - Executorの実装例
Callableインターフェイス
ここからは機能を解説いただきながら、サンプルコードを眺めます。
- スレッド間で処理を行うことができる
- ex. T1スレッドがFutureオブジェクトを作り,T2スレッドがそのFutureオブジェクトでsetの処理を行う
- set の処理が終わった段階で,T1がFutureからgetで値を取得する
Fork/Joinフレームワーク
- 1+2+3+4・・・ の総和をスレッドで分割して、結果をマージできるような処理が書ける
- Fork/Joinクラスを使う
Java8 の並行処理に関する新機能
- あまり多くないが、StreamAPI を並列処理できるようになっている
最後に、JDK9, 10 ではトピックは無かったことを補足して、コースは終了しました。
まとめ
このコースではコードを実行して、挙動を確かめながら、マルチスレッドプログラミングを理解しました。(私は機能だけですが…)
マルチスレッドを使う場面は限られるものの、その制御が難しいこと、一方で、バージョンアップを重ねながら、スレッドを制御できるようにしていることがわかりました。このような機構を考えられる人がいるんですね。。
マルチスレッドで制御できるようになると、Javaアプリケーション自体の高速化をほとんど網羅したと言えるのではないでしょうか。そういった意味でマルチスレッドはよい区切りかも知れません。
ちなみにGo言語だとマルチスレッドが書きやすい、と言われています。対比すると面白いかも知れませんね。
label SE カレッジの無料見学、資料請求などお問い合わせはこちらから!!
label SEカレッジを詳しく知りたいという方はこちらから !!
SEプラスにしかないコンテンツや、研修サービスの運営情報を発信しています。