[J2SE1.4] スレッド2

排他制御
・synchronizedメソッドを使った排他制御
・synchronizedブロックを使った排他制御

[synchronizedメソッドを使った排他制御]
アクセス修飾子+synchronized+戻り値+メソッド名(引数リスト){...}

synchronizedメソッドには
非staticなものとstaticなもの
がある

・非staticなsynchronizedメソッド
synchronizedメソッドにあるスレッドにアクセスすると、
他のスレッドはこのメソッドにはアクセス出来なくなる。
→先にアクセスしたスレッドの処理が終わるまで待つ。

import java.util.ArrayList;
public class Monitor {
  private ArrayList list = new ArrayList();
  public synchronized void addObj(Object obj) {
    list.add(obj);
  }
}

Thread1がaddObjを実行中の間、
Thread2はaddObjにアクセス出来ない。

複数スレッドからアクセスされるオブジェクトを
スレッドプログラミングで[モニタ]という。

import java.util.ArrayList;
public class Monitor {
  private ArrayList list = new ArrayList();
  public synchronized void addObj(Object obj) {
    list.add(obj);
  }
  public synchronized void removeObj(int idx) {
    list.remove(idx);
  }
  public void getSize(int idx) {
    list.size(idx);
  }
}

Thread1がaddObjを実行中の間、
Thread2はremoveObjにもアクセス出来ない。

1つのsynchronizedメソッドが実行されている場合、
そのクラスに存在する全てのsynchronizedメソッドにロックがかかる。

クラス内の全てのsynchronizedメソッドは、シンクロし合って1セットとなる。

ただし、
synchronizedキーワードがついていない部分は排他制御の対象外
Thread1がaddObjを実行中の間も、
Thread2はgetSizeを実行できる。

要注意
フィールドlistを扱うメソッドaddObjとremoveObjにsynchronizedをつけても、
フィールドlist自体にロックがかかっていないので、
private以外のアクセス修飾子の、synchronizedでないメソッドでlistに意図せぬ変更をし得る。


・staticなsynchronizedメソッド

import java.util.ArrayList;
public class Monitor {
  private static ArrayList list = new ArrayList();
  public static synchronized void staticAddObj(Object obj) {
    list.add(obj);
  }
  public static synchronized void staticRemoveObj(int i) {
    if (i < list.size()) {
      list.remove(i);
    }
  }
  public synchronized void addObj(Object obj) {
    list.add(obj);
  }
  public synchronized void removeObj(int i) {
    if (i < list.size()) {
      list.remove(i);
    }
  }
}

Thread1がstatidAddObjを実行中の間、
Thread2は
staticAddObjとstaticRemoveObjにはアクセス出来ないが、
addObjとremoveObjにはアクセス出来る。

Thread1がaddObjを実行中の間、
Thread2は
addObjとremoveObjにはアクセス出来ないが、
staticAddObjとstaticRemoveObjにはアクセス出来る。

非staticなsynchronizedメソッド同士
staticなsynchronizedメソッド同士
でシンクロする。



[synchronizedブロックを使った排他制御]
synchronizedメソッドでの排他制御:
全てのメソッドが対象になり、1クラスに1種類の排他制御しか出来ない。

synchronizedブロックでの排他制御:
多彩な排他制御の範囲を指定でき、複雑な排他制御も出来る。

synchronizedブロックの記述方法
synchronized (OBJECT) { ...... }
※OBJECT:
 ロック用オブジェクト
 synchronizedブロックを実行する為の権利みたいなもの
 ロック用オブジェクトはObjectインスタンスでもStringインスタンスでも何でも可。

01:import java.utilArrayList;
02:public class Minitor {
03:  public static ArrayList list = new ArrayList();
04:  private Object lockObj = new Object();
05:  public void addObj(Object obj) {
06:    synchronized(lockObj) {
07:      list.add(obj);
08:    }
09:  }
10:}

6行目でロック(ロックフラグ)を取得したスレッドのみが
synchronizedブロックの処理を続行できる。

Thread1がsynchronizedブロックを処理している間、
Thread2はlockObjを取得出来ないのでsynchronizedブロックを処理できない。

メソッド全体を排他制御の対象とするよりも、
メソッドの一部分だけを排他制御の対象とした方が、
排他制御の範囲を最小限に出来るため、パフォーマンスも上がる。


synchronizedブロックで複数種類の排他制御の実装
import java.util.ArrayList;
public class Monitor {
  public static ArrayList list1 = new ArrayList();
  public static ArrayList list2 = new ArrayList();
  
  private Object lockObj1 = new Object();
  private Object lockObj2 = new Object();
  
  public void addObj1(Object obj) {
    synchronized(lockObj1) {
      list.add(obj);
    }
  }
  public void addObj2(Object obj) {
    synchronized(lockObj2) {
      list.add(obj);
    }
  }
  
  public synchronized void removeObj1(int i){
    synchronized(lockObj1){
      if(i < list.size()){
        list1.remove(i);
      }
    }
  }
  public synchronized void removeObj2(int i){
    synchronized(lockObj2){
      if(i < list.size()){
        list1.remove(i);
      }
    }
  }
}

lockObjectはなんでもよい

import java.util.ArrayList;
public class Monitor {
  public static ArrayList list = new ArrayList();

  public void addObj1(Object obj){
    synchronized(this){
      list.add(obj);
    }
  }

  public int getSize(Object obj){
    return list.size();
  }
}

単にロックオブジェクトが[this]なだけ。
synchronizedブロック内を実行中の間は、
他のスレッドが自分自身にアクセスできないわけでナイ。

自分自身のロックフラグを持っていないスレッドが synchronized ブロック内を実行できないだけ。
[list]や[getSize]にはアクセス可能。

--------------------------------------------------
Q1 間違ってるのは?

a. public class Car
b. public abstract class Car
c. public class Car implements Vehicle
d. public synchronized Car extends AutoMobil

A1

d

synchronizedは、
synchronized メソッドかsynchronized ブロックしか使用不可

--------------------------------------------------
Q2 ThreadAがstaticAddObj()を実行してる間に、ThreadBがアクセスできるメソッドは?

01:import java.util.ArrayList;
02:public class Monitor {
03:  public static ArrayList list = new ArrayList();
04:  
05:  public static synchronized void static AddObj(Object obj) {
06:    list.add(obj);
07:  }
08:  
09:  public static synchronized void staticRemoveObj(int i) {
10:    if (i < list.size()) {
11:      list.remove(i);
12:    }
13:  }
14:  
15:  public synchronized void addObj(Object obj) {
16:    list.add(obj);
17:  }
18:  
19:  public synchronized void removeObj(int i) {
20:    if (0 < list.size()) {
21:      list.remove(i);
22:    }
23:  }
24:  
25:  public int getSize() {
26:    list.size();
27:  }
28:}

a. staticAddObj(Object obj)
b. staticRemoveObj(int i)
c. addObj(Object obj)
d. removeObj(int i)
e. getSize()

A2

×a. staticAddObj(Object obj)
×b. staticRemoveObj(int i)
○c. addObj(Object obj)
○d. removeObj(int i)
○e. getSize()

staticなsynchronizedメソッドは、他のstaticなsynchronizedメソッドとシンクロする。
非staticなsynchronizedメソッドは、他の非staticなsynchronizedメソッドとシンクロする。
→staticはstatic同士で、非staticは非static同士でシンクロする。

--------------------------------------------------
Q3 正しいのは?

1: public class Monitor {
2: private int num1;
3: private int num2;
4: void increase(){
5: num1 ++;
6: num2 ++;
7: System.out.println(num1 == num2);
8: }
9: }
10:
11: public class ThreadClass extends Thread {
12: private Monitor m;
13: public ThreadClass(Monitor m) {
14: this.m = m;
15: }
16: public void run() {
17: for (int i = 0; i < 1000; i++) {
18: m.increase();
19: }
20: }
21: }
22:
23: public class MainClass {
24: public static void main(String args[]){
25: Monitor m = new Monitor();
26: ThreadClass th1 = new ThreadClass(m);
27: ThreadClass th2 = new ThreadClass(m);
28: th1.start();
29: th2.start();
30: }
31: }

a. trueまたはfalseを表示することがある
b. 常にtrueを表示する
c. 常にfalseを表示する
d. 例外が発生する

A3

a

プログラムが実行されると二つのスレッドが
同一のMonitorクラスのオブジェクトmのincrease()メソッドを呼ぶ。
片方のスレッドがincrease()実行中にもう片方のスレッドがincrease()を実行すると、num1とnum2の値は等しくなくなるタイミングが出る。

--------------------------------------------------
Q4 必ずtrueと出力させるためには?

public class Monitor {
  private int num1;
  private int num2;
  void increase() {
    num1 ++;
    num2 ++;
    System.out.println(num1 == num2);
  }
}

public classThreadClass extends Thread {
  private Monitor m;
  public ThreadClass(Monitor m) {
    this.m = m;
  }
  public void run() {
    for (int i=0; i<1000; i++) {
      m.increase();
    }
  }
}

public class MainClass {
  public static void main(String[] args) {
    Monitor m = new Monitor();
    ThreadClass th1 = new ThreadClass(m);
    ThreadClass th2 = new ThreadClass(m);
    th1.start();
    th2.start();
  }
}

a.Monitorクラスを変更
 public class Monitor {
   private int num1;
   private int num2;
   void synchronized increase() {
     num1 ++; num2++;
     syso(num1 == num2);
   }
 }
b.Monitorクラスを変更
 public class Monitor {
   private int num1;
   private int num2;
   void increase() {
     synchronized(this) {
       num1 ++; num2++;
       syso(num1 == num2);
     }
   }
 }
c.ThreadClassを変更
 public class ThreadClass extends Thread {
  private Monitor m;
  public ThreadClass(Monitor m) {
    this.m = m;
  }
  public void synchronized run() {
    for (int i = 0; i < 1000; i++) {
      m.increase();
    }
  }
 }
c.ThreadClassを変更
 public class ThreadClass extends Thread {
  private Monitor m;
  public ThreadClass(Monitor m) {
    this.m = m;
  }
  public void run() {
    synchronized(this) {
      for (int i = 0; i < 1000; i++) {
        m.increase();
      }
    }
  }
 }

A4

b

--------------------------------------------------
Q5 deposit()が実行中だとして 正しいのは?

public class Monitor {
  private int account;
  public synchronized void deposit(int money) {
    account = account - money;
  }
  public synchronized void withdraw(int money) {
    account = account + money;
  }
  public int getAccount() {
    return account;
  }
}

a. 他のスレッドはこのオブジェクトのどのメソッドも実行できない。
b. 他のスレッドはwithdraw()メソッドのみ実行できる。
c. 他のスレッドはgetAccount()メソッドのみ実行できる。
d. 他のスレッドは全メソッドを実行できる。

A5

c

--------------------------------------------------
Q6 正しいのは?

import java.util.ArrayList;
public class Monitor {
  private static ArrayList list = new ArrayList();
  private Object lockObj1 = new Object();
  private Object lockObj2 = new Object();
  
  public void addObj1(Object obj) {
    synchronized(lockObj1) {
      list.add(obj);
    }
  }
  
  public void removeObj1(int i) {
    synchronized(lockObj1) {
      if (i < list.size()) {
        list.remove(i);
      }
    }
  }
  
  public void removeObj2(int i) {
    synchronized(lockObj2) {
      if(i < list.size()) {
        list.remove(i);
      }
    }
  }
}

a. コンパイルエラー。
b. addObj1のlist.add(obj)で例外が出うる。
c. removeObj1のlist.remove(i)で例外が出うる。
d. removeObj2のlist.remove(i)で例外が出うる。
e. 例外なし。

A6

c, d

同じlistを扱うのに異なるロックオブジェクトを使っているため
同時に複数スレッドからlistを変更してしまいうる。
で、IndexOutOgBoundsExceptionが出うる。



goto [J2SE1.4] INDEX

tag : SJC-P

2008-07-08 04:58 : __lang__java : コメント : 0 : トラックバック : 0 :
コメントの投稿
非公開コメント

« next  ホーム  prev »

search

ad



counter


tag cloud

category cloud