愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

個人的Java並行/非同期処理めも

仕事でJava使ってるわけでもないし、っていうかJava触ってたのって研究室でのごく一時期だけでJavaのジャの字も分かっていないのだけど、「体調が悪いときはひたすらダラダラインプットする」といういつも通りの行動を取っていたら「Java並行処理プログラミング」に突き当たった。という。

ちなみに、上の本より先の内容はないです。nioとかないです。結構今更な内容かと。

とりあえず

concurrent系のパッケージにあるデータ構造使う(ConcurrentHashMapとかBlockingQueueとか)。本の前半の「自分でsynchronizedしてほげほげする」の部分は噛ませ犬。いわゆる「ロックで効率よくthread safeな設計するのは難しいですよ」的な。

Runnable

http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Runnable.html

何がしたいのか分かりにくいけど、要はとある無名関数 or ブロック的なものを1つのスレッドに結びつけて実行するためのインタフェースぽい。計算した値は返せない。(内部で共有変数に突っ込むとかはできる)

Callable

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/Callable.html

値を返せるRunnable、っぽい。基本的にはこっちのほうがうれしいっぽい。ちなみにRunnableもCallableも、threadとか無視してrun()とかcall()を直で呼んじゃうこともできる。CallableはT型の値を返すます。

Future

いわゆるFutureパターン。

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/Future.html

FutureはV型の値をラップして、getするとその値が取れる。ただ、Futureは「まだ計算おわてないよー」的な状態も表すので、
getしたときにまだ内部の値がない時は値が計算されるまでblockする。cancelとかもできる。

FutureTask

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/FutureTask.html

RunnableとFutureを両方実装している。
Futureは「値をラップする何か」だけど、FutureTaskは「値を生成するCallableをラップする何か」。
なので、Callableから生成する。

BlockingQueue

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/BlockingQueue.html

threadによるいわゆるproducer-consumerパターンの実装を楽にしてくれるキュー。dequeueすると、データがある場合はデータが取れ、データない場合はデータ入るまでブロック。的な。サイズ制限をつけると、満杯のときにenqueueするとブロック。たぶんプログラム内部におけるジョブキュー的に使う。

Executor

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/Executor.html

昔この辺を調べたときはこれがよく分からなかったんだけど、どうやらJobのsubmitとJobの実行を分離するためのものらしい。

submitする部分はここで

  Runnable task = new Runnable() {
    public void run() {
      // do something
    }
  };
  executor.execute(task);

Executorのサブクラスでexecuteをオーバライドしてホゲホゲ。的な。

ExecutorService

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/ExecutorService.html

ExecutorはRunnableしかexecuteできないのが悲しい。それでは計算結果が受け取れないではないか。
そこでExecutorService。ExecutorServiceのsubmitはCallableを受け取り、Futureを返す。
やったねたえちゃん、値が取れるよ!

ちなみにFutureTaskはRunnableをimplementしているので、ExecutorにFutureTask経由でCallableを渡す、という方法で値を得ることもできるっぽい。

ExecutorCompletionService

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/ExecutorCompletionService.html

内部にExecutorとBlockingQueueをもち、Futureのdoneをオーバライドしてその中で自身をBlockingQueueにenqueueすることで、要は終わったJobから順にBlockingQueueから取り出す、という能力をExecutorに付け加える、的な。

Atomicほげほげ

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/atomic/AtomicInteger.html
http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/atomic/AtomicReference.html

Atomicほげほげを使うと、JavaレイヤからCAS命令を明示的に行うことができる。concurrentパッケージのコンテナも、これらを使って実装されているものがあるとかなんとか。

まとめ

おもしろかった。微妙な関係性だけど↓を思い出した。

http://togetter.com/li/115764