21
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

よくわからないJavaの並行処理

Last updated at Posted at 2017-06-09

よくわからないところをまとめます。
よくわかってないのに投稿してごめんなさい。
わかってる方教えてください。

ThreadとRunnable

Javaにおける並行処理=マルチスレッドプログラミング。

Threadはスレッドを扱うクラス。startでスレッドをスタートさせる。startの中でrunが動くので、Threadを継承してrunをOverrideする。

Runnableはエントリポイントとなるクラスのインタフェース。Threadに渡してstartで動かしてもらう。インタフェースなのでimplementしてrunを実装する。

新規スレッドをスタートさせることができるのはThreadクラスのみ。振る舞いはrun。Threadを継承するかRunnableを実装するかはTemplateMethodパターンとStrategyパターンのどちらを選ぶかという問題で、通常はクラスの依存性の観点からStrategyパターンを採択する。

よくわからないのが、ThreadがRunnableインタフェースを実装しているところ。
runを実装するので、間違ってはいないみたいだけどややこしい。普通に使う場合は、Runnableを実装していることは忘れてしまっていいと思う。どこで役に立つのかわからない。
ここについては、Javaの1.0がリリースされたときも議論があったらしく、つまりよくわからないものらしい。

参考
きしだのはてな ThreadとRunnable

吉田誠一のホームページ Javaのスレッドとメモリリーク

Executorフレームワーク

Threadは1回使ったらもう使えないし、だからといって大量に生成するとリソースを圧迫する、ということでコントロールするお仕事を任せるためにJava 1.5で導入されたのがExecutorフレームワーク。
コントロールを任せることで、プログラマはタスクの設計に注力できる。
「スレッドをコントロールする」ってのはつまりWorkerThreadパターンによってスレッドをプールして効率的に働いてもらおうってことっぽい。

ExecutorはCommandパターンを実現するので、スレッドは意識せずにタスクをexecuteすればいい。

Executorに終了と追跡の機能を追加したのがExecutorServiceインタフェース。shutdownでタスクの受付を不可にしたり、submitの戻り値Futureでタスクを追跡可能。

FutureクラスはFutureパターンの実装。submitでFutureを得る場合はタスクとしてCallableを渡す。CallableはRunnableとほとんど同じように使う。Submitで結果を得るかどうかの違いだけ。CallableはRunnableを継承していない。SubmitはRunnableを受取ることもできるけど、結果が変わってくるらしい(どう変わるのかはよくわからない。Futuerがnull?)。submitするならCallableを使っとけば問題ない。CallableとRunnableの違いがややこしい。RunnableでFuture返してくれればいいのに。
RunnableでありFutureでもあるRunnableFutureインタフェースなんてものもあってわけがわからない。

ExecutorおよびExecutorServiceは普通、自分では実装しない。Executorsをファクトリとして取得する。ScheduledなExecutorServiceなどあるので、APIを確認されたし。
ExecutorsにはRunnableからCallableへの変換器もあるので、やっぱりCallableはRunnableなんだと思う。

あとこのへんはもうパターン名で会話する感じなのでデザインパターンを知らないとつらいっす。

参考
働かないプログラマのメモ帳 JavaのExecutorが便利

argius note Concurrency Utilitiesを使った並列処理・マルチスレッドのおさらい

ForkJoinフレームワーク

Executorフレームワークを、再帰的に小さな単位に分割できる作業向けにチューニングし、最高速を目指したフレームワーク。WorkStealingアルゴリズムを採用して、高速化を図っている。Java 1.7で導入。

WorkStealingはワークをスティールするアルゴリズム。タスクを細かくフォークしてキューにセットし、ワーカースレッドがキューからタスクを受取る際に、キューのタスクが全て完了している場合は他のキューのタスクをスティールする。

ForkJoinフレームワークのコアな機能となるForkJoinPoolはExecutorServiceの実装クラスであり、ForkJoinTaskはFutureの実装クラス。ForkJoinPoolがWorkStealingアルゴリズムを実装している。

ForkJoinTaskは抽象クラスだけど直接継承しない。結果を返さない場合はRecursiveAction、結果を返す場合はRecursiveTaskを継承してタスククラスを実装する。ForkJoinPoolはexecuteやsubmitからcomputeをコールするので、computeに処理を実装する。結果はFuture。

ここまで何度も出てきたように、マルチスレッドプログラミングでは結果を返すか返さないかを選ぶ必要がちょいちょいある。違いはなんなのかよくわからなかった。返さないほうが高速とかあるんだろうか。

ParallelStream

関数型のパラダイムを取り入れたStreamAPIの並行処理機能。Java 1.8で導入。

Collectionなどの集合からStreamを生成し、parallel一発で並行処理を実現できるため非常に手軽。手軽にできるのは関数型インタフェースを用いて実装することで、副作用のない関数型の処理を簡潔に記述できるから。

並行処理において副作用がある(状態を変化させる)処理はタブーだけど、Javaには副作用のある関数インタフェースも存在するので注意すること。

ただ細かいチューニングが必要な場合などは、Executorフレームワークを用いたほうがいいらしい。
シリアルな方が早い場合もあるし、手軽だからといって何でもかんでもparallelにすればいいってもんでもない。使い分けの線引がむずかしくてよくわからない。

参考
Legend of Java Concurrency/Parallelism -Yuichi Sakuraba

21
28
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?