マルチスレッドプログラミングってよくわかんないよねー
研究のためにJavaでマルチスレッドなプログラムを書かなきゃならなくなった。そのときに疑問に思ったことがあるので書き置いておく。
下のようなコードを考える。
import java.util.concurrent.*; class Test { public static void main(String[] args) { ScheduledExecutorService schedular = Executors.newSingleThreadScheduledExecutor(); ScheduledTask scheduledTask = new ScheduledTask(); ScheduledFuture<?> handle = schedular.scheduleAtFixedRate(scheduledTask, 0, 1, TimeUnit.SECONDS); try { Thread.sleep(10000); } catch(InterruptedException e) { } synchronized(scheduledTask) { handle.cancel(true); System.out.println("後片付け"); } schedular.shutdown(); } private static class ScheduledTask implements Runnable { public synchronized void run() { System.out.println("繰り返し"); } } }
このコードは、"後片付け"の処理の後に"繰り返し"の処理が発生しないように意図して書かれている。でも、本当にそれが実現できているのかが気になっている。要するに下記のような動作が起きるか否かという話。
- 前回のタスクの実行から1秒経過したのでコンテキストスイッチが発生。[schedularのスレッド]が実行状態になる。
- [schedularのスレッド] scheduledTask.run()メソッドを呼び出す準備をして、scheduledTaskが起動済みになる。scheduledTask.run()メソッドはまだ呼び出されていない。
- コンテキストスイッチが発生。[mainのスレッド]が実行状態になる。(←ここが問題!)
- [mainのスレッド] synchronized(scheduledTask)ブロックに入る。ブロックを抜けるまではscheduledTask.run()メソッドの実行はブロックされる。
- [mainのスレッド] handle.cancel(true)で起動前のscheduledTaskの実行を取り消す。しかしscheduledTaskは起動済みなので何も起きない。
- [mainのスレッド] "後片付け"の処理を実行する。
- [mainのスレッド] synchronized(scheduledTask)ブロックを抜ける。
- コンテキストスイッチが発生。[schedularのスレッド]が実行状態になる。
- [schedularのスレッド] scheduledTask.run()メソッドを呼び出す。
- [schedularのスレッド] "繰り返し"の処理を実行する。
- [schedularのスレッド] scheduledTask.run()メソッドを抜ける。
もしこんな感じで処理が進むと、"後片付け"の処理の後に"繰り返し"の処理が発生してしまうことになる。問題は上の2.のようなコンテキストスイッチが発生するのかどうかである。現実的に考えると、[schedularのスレッド]が起きたばかりなので、そんなにすぐ2.のコンテキストスイッチが発生することはないんじゃないかと思う。でも、絶対にありえないとは言い切れないのでJavaのほうで対策が施されてないと困ってしまう。
そもそも、上のコードの実現の仕方がダメすぎるという可能性もある。他のしかるべき方法ならば悩むことなく実現可能なのかもしれない。誰か教えてください。
そして話がいきなり変わるが、親知らずを抜いたときの顔の腫れが全然引く気配がない。この腫れのせいで口が動かしづらいので、食べ物を食べてもさっぱりおいしく感じない。これでもう片方の親知らずも抜くとかありえなくね?