プログラミング 第16回

プログラミング 第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