35
Java 並行処理 今昔物語 NDS37 @civic

第37回NDS Java並行処理 今昔物語

Embed Size (px)

DESCRIPTION

Javaの並列・並行処理について、標準ライブラリのユーティリティなどをピックアップして紹介

Citation preview

Page 1: 第37回NDS Java並行処理 今昔物語

Java 並行処理 今昔物語NDS37 @civic

Page 2: 第37回NDS Java並行処理 今昔物語

今回話す内容

• Java の並行処理モデルについて

• 並行処理・非同期処理実行方法

• スレッドセーフのために

Page 3: 第37回NDS Java並行処理 今昔物語

こちらを参考にピックアップ

• Java並行・並列・非同期処理チートシート - Qiita

• http://qiita.com/yohhoy/items/bc119324d2b69570597b

Page 4: 第37回NDS Java並行処理 今昔物語

Javaの並行処理モデルについて

Page 5: 第37回NDS Java並行処理 今昔物語

並行処理モデル

• スレッドモデル

• 共有データ

• ロック

Page 6: 第37回NDS Java並行処理 今昔物語

concurrent関係 歴史年表

Page 7: 第37回NDS Java並行処理 今昔物語

歴史年表Version Class/ Package / Syntax Year1.0 Thread / synchronized `961.2 Synchronized Collection `98

ThreadLocal1.3 Timer `005 ConcurrentHashMap `04

Semaphore, Lock …java.util.atomic

7 Fork / Join `118 parallelStream `14

Page 8: 第37回NDS Java並行処理 今昔物語

並行処理・非同期処理 実行方法あれこれ

Page 9: 第37回NDS Java並行処理 今昔物語

Thread

Java 1.0 から。Runnable インターフェースを実装。Threadの生成、開始、排他制御、同期は自分でやらなければならない。

Page 10: 第37回NDS Java並行処理 今昔物語

Thread

Thread th = new Thread(new Runnable(){ public void run(){ //このスレッドでの処理 } }); th.start(); //スレッドの実行開始

Page 11: 第37回NDS Java並行処理 今昔物語

ちなみにJava8なら

Thread th = new Thread(()->{ //このスレッドでの処理 }); th.start(); //スレッドの実行開始

Page 12: 第37回NDS Java並行処理 今昔物語

Synchronized

Java 1.0 から。Threadでの排他制御。言語として採用されている。

Page 13: 第37回NDS Java並行処理 今昔物語

Synchronizedpublic synchronized void add(){ //メソッド全体 //このメソッドを同時に実行できるのは //1スレッドのみ } !Object lock = new Object(); synchronized(lock){ //ロックオブジェクト //とブロック // ... }

Page 14: 第37回NDS Java並行処理 今昔物語

TimerJava 1.3から。バックグラウンドでの遅延実行。単一のスレッドが割り当てられ、指定時間後または一定間隔にタスクを実行する。

javascriptのsetTimeout, setInterval的な。

単一スレッドで実行するのでタスクの実行に時間がかかるとスレッドを専有し、後続のタスクの実行に影響する。

Page 15: 第37回NDS Java並行処理 今昔物語

Timer

Timer timer = new Timer(); timer.schedule(new TimerTask(){ public void run(){ // ... } }, 1000, 5000); //1秒後に開始、5秒間隔

Page 16: 第37回NDS Java並行処理 今昔物語

Executor

Java5から。別スレッドでのタスク実行をExecutorServiceに登録して実行。ExecutorServiceはシングルスレッドだったり固定数のスレッドプールだったりできる。

Futureを使うことで、別スレッドでのタスク実行結果を受け取るのが楽になる。

Page 17: 第37回NDS Java並行処理 今昔物語

ExecutorExecutorsService es = Executors.newSingleThreadExecutor(); Future<Integer> future = es.submit(new Callable<>(){ // ... return ret; }); !Integer ret = future.get(5, TimeUnit.SECONDS); //タスクの終了までブロック( 大5秒)

Page 18: 第37回NDS Java並行処理 今昔物語

ExecutorsServiceの実装ExecutorsServiceの実装を取り替えることで、タスク実行スレッドを変化させることができる。 !ExecutorsService es = Executors.newFixedThreadPool(3); //3スレッドでタスク消化 !ExecutorsService es = Executors.newCachedThreadPool(); //必要に応じて新規スレッド生成・廃棄

Page 19: 第37回NDS Java並行処理 今昔物語

Fork/JoinJava7から。Executorsよりも、細粒度のタスクを実行する場合にExecutorsよりも高速。

ForkJoinPoolに、RecursiveTask(返り値あり)またはRecursiveAction(返り値なし)を登録して並列に実行する。

I/O処理中心のタスクよりも、CPU処理中心のタスクを実行する場合に効果的。

Page 20: 第37回NDS Java並行処理 今昔物語

Fork/Joincompute(){ if (作業.サイズ < しきい値){ return doWork(作業); } else { f1 = fork(分割した作業の前半); f2 = fork(分割した作業の後半); 2つのfork処理がjoinするまで待機 } }

Page 21: 第37回NDS Java並行処理 今昔物語

parallelStream

Java8から。streamを並列処理する。内部的にFork/Joinを使っている。

Page 22: 第37回NDS Java並行処理 今昔物語

parallelStreamint total = IntStream.range(1, 1000) .parallel() .sum(); !myArraylist.parallelStream().forEach((elm)->{ //各要素の処理 }); !※java8 のConsumerインターフェースのラムダ式

Page 23: 第37回NDS Java並行処理 今昔物語

スレッドセーフのために

Page 24: 第37回NDS Java並行処理 今昔物語

Synchronized Collection

Java1.2から。 複数のスレッドで安全にデータを操作できるCollection

ArrayList, HashMapなどは複数のスレッドから触ると意図しないデータ操作になりかねない。

Page 25: 第37回NDS Java並行処理 今昔物語

Synchronized Collection

List<String> syncList = Collections.synchronizedList(new ArrayList<>()); !Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

Page 26: 第37回NDS Java並行処理 今昔物語

ConcurrentHashMap

Java5から。java.util.concurrent.ConcurrentHashMapは、スレッドセーフでありながら、Collections.synchronizedMapよりも高い更新平行性をサポート。

Page 27: 第37回NDS Java並行処理 今昔物語

CopyOnWriteArrayList

Java6から。java.util.concurrent.CopyOnWriteArrayListは、スレッドセーフ。イテレーションもロックされない。

変更操作のたびに新しいコピーが生成される。変更がほとんどなくイテレーションが多い操作に有効。

Page 28: 第37回NDS Java並行処理 今昔物語

AtomicInteger

Java5から。

AtomicInteger、AtomicLongのようなクラスはスレッドセーフでロックフリーな変数として使用できる。

Page 29: 第37回NDS Java並行処理 今昔物語

AtomicInteger

AtomicInteger i = new AtomicInteger(1); i.incrementAndGet(); //i++

Page 30: 第37回NDS Java並行処理 今昔物語

シンクロナイザ

java5から。マルチスレッドでの同期方法をサポートするためのユーティリティ。

いろいろある。Semaphore, CountDownLatch, CyclicBarrier, Phaser, Exchanger...

Page 31: 第37回NDS Java並行処理 今昔物語

CountDownLatchCountDownLatch latch = new CountDownLatch(3); for (int n = 0; n < 3; n++){ Thread th = new Thread(() -> { // ... なんらかの処理 latch.countDown(); }); th.start(); } latch.await(2, TimeUnit.SECONDS); //latchが0になるのを 大2秒wait

Page 32: 第37回NDS Java並行処理 今昔物語

Semaphore

• 同時実行可能なスレッドを制限

Page 33: 第37回NDS Java並行処理 今昔物語

CyclicBarrier

• CountDownLatch のように待ち合わせ

• 解放後に再利用できる循環式

Page 34: 第37回NDS Java並行処理 今昔物語

Exchanger

• 2つのスレッド間でデータ交換

Page 35: 第37回NDS Java並行処理 今昔物語

まとめ• 並行処理・非同期処理の実行方法

• Thread, Timer, Executor, Fork/Join

• 非同期処理のためのユーティリティ

• java.util.concurrent

• https://github.com/civic/nds-java-concurrent