Java Gold 例題 同期制御

```java
package threadTest;

public class SynchronizeTest {
	public static void main(String[] args) {
		Market market = new Market();
		ThreadFirst tf = new ThreadFirst(market);
		ThreadSecond ts = new ThreadSecond(market);
		tf.start();
		ts.start();
	}
}

class ThreadFirst extends Thread {
	private Market market;
	public ThreadFirst(Market m) {
		market = m;
	}
	public void run() {
		for(int i = 0; i < 4; i++) {
			int num = (int)(Math.random() * 10 + 1);
			market.wholesale(num);
		}
	}
}

class ThreadSecond extends Thread {
	private Market market;
	public ThreadSecond(Market m) {
		market = m;
	}
	public void run() {
		for(int i = 0; i < 4; i++) {
			int num = (int)(Math.random() * 4 + 1);
			market.sell(num);
		}
	}
}

class Market {
	private int productStock = 0;
	synchronized void wholesale(int num) {
		while(productStock != 0) {
			try { 
				wait();
			} catch(InterruptedException e) {
			}
		}
		notifyAll();
		productStock += num;
		System.out.println("入荷しました! 商品の在庫 : " + productStock);
	}
	
	synchronized void sell(int num) {
		while(productStock == 0) {
			try {
				wait();
			} catch(InterruptedException e) {
			}
		}
		notifyAll();
		productStock -= num;
		
		if(productStock <= 0) {
			num += productStock;
			productStock = 0;
			System.out.println("販売数 : " + num);
			System.out.println("売り切れ! ");
		} else {
			System.out.println("販売数 : " + num);
		}
	}
}
```

このコードの実行結果として当てはまらないものを2つ答えてください。
A
入荷しました! 商品の在庫 : 1
販売数 : 1
売り切れ! 
入荷しました! 商品の在庫 : 1
販売数 : 1
売り切れ! 
入荷しました! 商品の在庫 : 2
販売数 : 1

B
入荷しました! 商品の在庫 : 1
販売数 : 1
売り切れ! 
入荷しました! 商品の在庫 : 1
販売数 : 0
販売数 : 1
売り切れ! 
入荷しました! 商品の在庫 : 4


C
入荷しました! 商品の在庫 : 4
販売数 : 1
販売数 : 1
販売数 : 1

D
入荷しました! 商品の在庫 : 4
販売数 : 1
販売数 : 3
売り切れ! 
入荷しました! 商品の在庫 : 1
販売数 : 1
売り切れ! 
入荷しました! 商品の在庫 : 4

E
販売数 : 3
販売数 : 4
販売数 : 3
入荷しました! 商品の在庫 : 1
入荷しました! 商品の在庫 : 2

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

B, E
Bの結果では販売数0が含まれていますが、販売数は1~4の整数になります。

```java
int num = (int)(Math.random() * 4 + 1);
```
`Math.random()`では0以上1未満の`double`値が生成されます。
その結果に+1したものをint型へ変換しています。
これにより1から4の整数をランダムに取得することができます。

例えば`random()`の結果が0.9111...`だった場合、0.9111... * 4 = 3.6444...となります。
その結果に+1するので4.6444...となります。

`(int) 4.6444` 
intへキャストされると、小数点切り捨てにより4となります。

よって`int num = (int)(Math.random() * 4 + 1)`
変数`num`には1～4の整数が代入されます。

Eの結果では最初の出力が販売になっています。
`productStock`が0の状態で`sell()`が呼ばれた時、`wait()`メソッドで待機状態になります。

```java
class Market {
	private int productStock = 0;
    synchronized void sell(int num) {
    while(productStock == 0) {
        try {
            wait();
        } catch(InterruptedException e) {
        }
    }
    notifyAll();
    productStock -= num;
  ```
  `ThreadFirst`の`wholesale`メソッドが呼び出されることで、`productStock`に1~10の整数が代入され、`notifyAll()`により`InterruputedException`をcatchし、`ThreadSecond`の処理が再開されます。
  ```java
synchronized void wholesale(int num) {
		while(productStock != 0) {
			try { 
				wait();
			} catch(InterruptedException e) {
			}
		}
		notifyAll();
		productStock += num;
  ```
よって、最初の処理は入荷となります。

補足

このコードは、2つのスレッドが互いに競合しないように、商品在庫を同期化して管理する方法を示しています。`synchronized`、`wait()`, `notifyAll()` を使って、複数スレッドが協調して在庫の入荷と販売を行うシミュレーションをしています。

`SynchronizeTest`クラス

```java
public class SynchronizeTest {
    public static void main(String[] args) {
        Market market = new Market();
        ThreadFirst tf = new ThreadFirst(market);
        ThreadSecond ts = new ThreadSecond(market);
        tf.start();
        ts.start();
    }
}
```
`ThreadFirst` と `ThreadSecond` の作成と開始。
2つのスレッド (tf と ts) を作成し、それぞれ Market オブジェクトを共有します。
tf は入荷、ts は販売を担当し、それぞれのスレッドが start() メソッドで並行して動作します。

`ThreadFirst`クラス
```java
class ThreadFirst extends Thread {
    private Market market;
    public ThreadFirst(Market m) {
        market = m;
    }
    public void run() {
        for(int i = 0; i < 4; i++) {
            int num = (int)(Math.random() * 10 + 1);
            market.wholesale(num);
        }
    }
}
```
`run()` メソッド: スレッドが`start()`する際に呼び出されるコードです。
Math.random() でランダムな数の商品の入荷処理を3回まで行います。
`Market`クラスに定義された在庫の入荷を意味する`wholesale` メソッドを呼び出します。

```java
synchronized void wholesale(int num) {
        while(productStock != 0) {
            try { 
                wait();
            } catch(InterruptedException e) {
            }
        }
        notifyAll();
        productStock += num;
        System.out.println("入荷しました! 商品の在庫 : " + productStock);
    }
```

`synchronized` で同期化されており、複数のスレッドが同時にこのメソッドにアクセスできないようにします。
入荷は在庫がゼロのときにのみ行われ、入荷が終わると他のスレッドに通知します (notifyAll() を呼び出します)。



`ThreadSecond`クラス

```java
class ThreadSecond extends Thread {
    private Market market;
    public ThreadSecond(Market m) {
        market = m;
    }
    public void run() {
        for(int i = 0; i < 4; i++) {
            int num = (int)(Math.random() * 4 + 1);
            market.sell(num);
        }
    }
}
```
ランダムな数の商品の販売処理を3回まで行います。
`Market`クラスに定義された商品を販売するメソッド`sell`を呼び出します。

```java
synchronized void sell(int num) {
        while(productStock == 0) {
            try {
                wait();
            } catch(InterruptedException e) {
            }
        }
        notifyAll();
        productStock -= num;
        
        if(productStock <= 0) {
            num += productStock;
            productStock = 0;
            System.out.println("販売数 : " + num);
            System.out.println("売り切れ! ");
        } else {
            System.out.println("販売数 : " + num);
        }
    }
```
販売は在庫がある場合にのみ行われ、販売後は在庫数を減らします。
在庫が0以下になると、「売り切れ」を表示します。また、販売が終わると他のスレッドに通知します (notifyAll() を呼び出します)。

`Market`クラス

```java
class Market {
    private int productStock = 0;

    synchronized void wholesale(int num) {
        while(productStock != 0) {
            try { 
                wait();
            } catch(InterruptedException e) {
            }
        }
        notifyAll();
        productStock += num;
        System.out.println("入荷しました! 商品の在庫 : " + productStock);
    }

    synchronized void sell(int num) {
        while(productStock == 0) {
            try {
                wait();
            } catch(InterruptedException e) {
            }
        }
        notifyAll();
        productStock -= num;
        
        if(productStock <= 0) {
            num += productStock;
            productStock = 0;
            System.out.println("販売数 : " + num);
            System.out.println("売り切れ! ");
        } else {
            System.out.println("販売数 : " + num);
        }
    }
}
```

一行目のメンバ`productStock`の数によって、スレッドを制御します。

在庫が0ではない時(在庫が1以上あるとき)`wholesale()`メソッドの`while(productStock != 0)`が`true`となり、`wait()`されます。`sell()`メソッドの`notifyAll()`によって処理を再開します。
初期状態では`private int productStock = 0;`となっているため、在庫は0になります。
よって
```java
notifyAll();
productStock += num;
System.out.println("入荷しました! 商品の在庫 : " + productStock);
```
が必ず最初に実行されます。
その後、`sell()`メソッドにより在庫が0になるまで待機状態となります。
在庫が0の時、`sell()`メソッドの`while(productStock == 0)`が`true`となります。
初期状態では在庫0なので、割り込みが発生するまで待機状態になります。
商品が入荷されると処理を再開します。
在庫から販売数を引きます。
もし、販売数が在庫数以上の場合。
`productStock -= num;`
在庫数が0以下、`if(productStock <= 0)`が`true`になります。
`num += productStok`
`productStock = 0;`
とすることで在庫数を0に
販売数を販売前の在庫数に設定します。

例えば、在庫数1で販売数9の場合
`productStock -= num;`
1 -= 9
num = 9
productStock = -8

`num += productStock`
9 += -8
productStock = -8
num = 9 - 8 = 1
`productStock = 0`

在庫1のときに9個販売したあと、在庫は0、販売数は1になります。


https://qiita.com/T-ngtj/private/df254aacd05be698199a