プログラミング 第16回 スレッド競合の解決 [email protected] プログラミング第16回 1 解決法 • synchronized キーワードで排他制御 • スレッドの休止,終了 プログラミング第16回 2 GoodBank.java (List 16-5, p.164)addMoneyメソッド public synchronized void addMoney(int money){ int currentValue=value; System.out.println(Thread.currentThread() +"が addMoney に入りました。"); value+=money; if(currentValue+money != value){ System.out.println(Thread.currentThread() +"で矛盾が発生しました!"); System.exit(-1); } System.out.println(Thread.currentThread() +"がaddMoneyから出ました。"); プログラミング第16回 } 3 GoodBankTest.java(List 16-6, p.165) public class GoodBankTest extends Thread{ GoodBank bank; public GoodBankTest(GoodBank bank){ this.bank=bank; } プログラミング第16回 4 public void run(){ GoodBankTest.java (2) while(true){ bank.addMoney(100); bank.addMoney(-100); } } public static void main(String[] args){ GoodBank bank=new GoodBank(); new GoodBankTest(bank).start(); new GoodBankTest(bank).start(); } } プログラミング第16回 5 synchronized ここは1人しか入 れません • メソッドに入って仕事をする手順 – synchronizedメソッドに入って鍵をかける – 1人で仕事をする – 鍵をはずしてsynchronizedメソッドから出る • 鍵はインスタンスごと プログラミング第16回 6 スレッドを止めるには • 自分で終了したいなら,runメソッドを終了さ せばよい。 • 自分以外のスレッドを終了させたいときは, それが自主的に終了できるような仕掛けを 変数で用意しておく。例えば,次のスライド にあるようなrunningという変数を使う。 プログラミング第16回 7 Runner(List 16-7, p.170) class Runner extends Thread { private boolean running=true; public void stopRunning(){ running=false; } public void run(){ while(running){ doCommand(); } } } プログラミング第16回 8 Sleepの利用: Periodic.java(List 16-8, p.171) public class Periodic { public static void main(String[] args){ for(int i=0; i<5; i++){ int tm=i*1000; System.out.println("Start sleep:tm="+tm); try{ Thread.sleep(tm); }catch(InterruptedException e){ } } } } プログラミング第16回 9 実行結果 Start Start Start Start Start sleep:tm=0 sleep:tm=1000 sleep:tm=2000 sleep:tm=3000 sleep:tm=4000 プログラミング第16回 10 別解: Periodic2.java(List 16-9, p.172) public class Periodic2 extends Thread { public static void main(String[] args){ for(int i=0; i<5; i++){ int tm=i*1000; System.out.println("Start sleep:tm="+tm); try{ sleep(tm); }catch(InterruptedException e){ } } } } プログラミング第16回 11 スレッドの終了をまつ • joinメソッド:当該スレッドインスタンスの終 了をまつ。 – void join(long msec)でタイムアウトつき。 • sleepやjoinはInterruptedException例外を throwする場合があるので,try-catchブロッ クに囲む必要がある。 プログラミング第16回 12 JointTest.java (List 16-10, p.173) public class JoinTest extends Thread { public static void main(String[] args){ JoinTest th=new JoinTest(); System.out.println("main:はじめ"); th.start(); System.out.println("main:終了待ちに入る"); try{ th.join(); }catch(InterruptedException e){ System.out.println(e); } System.out.println("main:おわり"); } プログラミング第16回 13 JoinTest.java (2) public void run(){ System.out.println("run:スレッド開始"); try{ sleep(3000); }catch(InterruptedException e){ System.out.println(e); } System.out.println("run:スレッド実行終了"); } } プログラミング第16回 14 実行結果 main:はじめ main:終了待ちに入る run:スレッド開始 run:スレッド実行終了 main:おわり プログラミング第16回 15 スレッド同士の待ち合わせ • Producer-Consumerの例題 – スレッドPは,データを次々に作って配列に格 納する。 – スレッドCは,配列に格納されたデータを次々 に処理する。 プログラミング第16回 16 ProducerConsumer.java (List 16-11, p.175) public class ProducerConsumer { public static void main(String[] args){ Buffer buffer=new Buffer(3); Producer producer=new Producer(buffer); Consumer consumer=new Consumer(buffer); producer.start(); consumer.start(); } } プログラミング第16回 17 Buffer.java (1) class Buffer{ int[] intbuf; int start; int count; public Buffer(int size){ intbuf=new int[size]; start=0; count=0; } プログラミング第16回 18 public synchronized void append(int n){ while(count >= intbuf.length){ System.out.println( Thread.currentThread().getName() +" wait:バッファの空きを待つ"); try{ Buffer.java (2) wait(); }catch(InterruptedException e){ } } int end=(start+count)%intbuf.length; intbuf[end]=n; count++; notifyAll(); } プログラミング第16回 19 Buffer.java (3) public synchronized int remove(){ while(count==0){ System.out.println( Thread.currentThread().getName() +" wait:データを待つ"); try{ wait(); }catch(InterruptedException e){ } } プログラミング第16回 20 Buffer.java (4) int n=intbuf[start]; start=(start+1)%intbuf.length; count--; notifyAll(); return n; } } プログラミング第16回 21 Bufferの動作 始めは空 start count 0 0 0をappend 0 0 1 1をappend 0 1 0 2 0をremove 0 1 1 1 1をremove 0 1 2 0 2をappend 0 1 2 2 1 3をappend 3 1 2 2 2 4をappend 3 4 2 2 3 プログラミング第16回 22 Producer.java (1) class Producer extends Thread{ Buffer buffer=null; public Producer(Buffer buffer){ this.buffer=buffer; } public void run(){ for(int i=0; i<1000; i++){ buffer.append(produce(i)); } buffer.append(-1); } プログラミング第16回 23 private int produce(int n){ sleep_randomly(); Producer.java (2) System.out.println("Producer:" +getName()+"は"+n+"を生産完了"); return n; } public void sleep_randomly(){ try{ int n=(int)(Math.random()*10000); sleep(n); }catch(InterruptedException e){ } } } プログラミング第16回 24 Producer の動作 • データを生産する。 • 生産したデータをBufferにappendする。 • 1000個のデータを生産したら最後に-1を 終わりの目印としてappendする。 プログラミング第16回 25 class Consumer extends Thread{ Consumer.java (1) Buffer buffer=null; public Consumer(Buffer buffer){ this.buffer=buffer; } public void run(){ while(true){ int n=buffer.remove(); if(n==-1){ break; } consume(n); } } プログラミング第16回 26 Consumer.java (2) private void consume(int n){ System.out.println("Consumer:" +getName()+"は"+n+"を消費中"); sleep_randomly(); } public void sleep_randomly(){ try{ int n=(int)(Math.random()*10000); sleep(n); }catch(InterruptedException e){ } } } プログラミング第16回 27 Consumer の動作 • データを消費する。 • Bufferからデータをremoveして,画面に表 示する。 • 終わりの目印の-1を受け取ったら終了す る。 プログラミング第16回 28 注意事項 • synchronized ブロック/メソッドの中でwait してよい。waitはそのスレッドが持っている そのオブジェクトのロックを解除する。 • waitはObjectクラスのメソッド。 プログラミング第16回 29 課題 • 問題16-1から7まで • 余裕があったら問題16-8~11 プログラミング第16回 30
© Copyright 2025 ExpyDoc