Javaで安全なスレッドを作りましょう

##背景##
新幹線の切符は100枚があります。番号は1から100までです。現在、それらの切符を全部売っていくことにしたいです。
通常一つの窓口を設置して、順番に販売していくことはいいですが、乗客は多くなると、窓口のかかりの人は手が回れないですので、もう一つの窓口を設置しました。
そして、現在窓口１と窓口２があります。窓口１と窓口２は一緒に新幹線の切符を販売することになりました。


ここの窓口はスレッドとして理解しても大丈夫です。では、窓口をどうやって設置しますか？

##スレッドの基本的な使い方##

###使い方１ Threadを継承したクラスを作成します###
```java
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new MyThread();
        Thread thread2 = new MyThread();
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}

class MyThread extends Thread {
    int ticket = 100;
    @Override
    public void run() {
        super.run();
        while (true) {
            if (ticket <= 0) {
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + +ticket);
            ticket -= 1;
        }
    }
}
```

JDKが用意したThreadクラスがあります、Threadを継承したカスタマイズクラスを生成して、runメソッドをオーバーライドします。***切符販売処理をrunメセッドの中に書きます。***
各スレッドからrunメソッドを実行させるためには、各スレッドのrunメソッドを呼び出します。

JDKのソースコードから確認したら、start()を呼び出すことによって、以下の処理が行われます。

- Causes this thread to begin execution
    - 新しいスレッドを立ち上げます
- the Java Virtual Machine calls the run method of this thread
    - 立ち上げられたスレッドでrunメソッドの中身を実行されます

***run()だけをよびだすと、runメソッドの中身を実行されます、新しいスレッドが始まらない。mainスレッドでrunの中身が実行されることと同じことになります。***


<dl>
<dt>正しい使い方です。</dt>
<dd>myThread.start()</dd>

<dt>メインスレッドでrunメソッドを実行させることになります。</dt>
<dd>myThread.run()</dd>
</dl>

<details><summary>アンドロイド開発の話</summary>
昔アンドロイドの開発をした時に、AsyncTaskを実行しようとして、executeではなく、oInBackgroundを呼び出して、エラーが発生された原因はまさか同じような原因です。
MyAsyncTask.execute(params) -> 正しい使い方です。
MyAsyncTask.oInBackground() main threadでdoInBackgroundコールバック メソッドを実行させます。
</details>



###使い方2: Runableを実例化したインスタンスを作成して、Threadの構造関数の引数として渡します###

```java
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunable());
        Thread thread2 = new Thread(new MyRunable());
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}

class MyRunable implements Runnable {
    int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if (ticket <= 0) {
                break;
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + +ticket);
            ticket -= 1;
        }
    }
}

```

###課題:共通データをアクセスする時###
新幹線の切符はそれぞれの販売センター独自で販売するものではなく、すべて販売センターが共通で使うものにんあるべきです
そうしないと、重複な切符が販売されてしまいます

```sh
thread 1st:100
thread 2nd:100  // ここスレッド1とスレッド2が99番目の切符が販売されてしまいました。
thread 1st:99
thread 2nd:99
thread 2nd:98
```


####対策1: Threadを継承したクラスを作成した場合####
マルチスレッドから操作するデータをクラスのstatic辺数にします。そうしたら、スレッド達から操作する辺数はそれぞれのスレッドに所属するものではなく、唯一のMyThreadクラスに所属されることになります。

```java
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new MyThread();
        Thread thread2 = new MyThread();
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}

class MyThread extends Thread{
    static int ticket = 100; // 共通変数
    @Override
    public void run() {
        super.run();
        while (true){
            if (ticket <= 0) {
                break;
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(Thread.currentThread().getName() + ":" + + ticket);
            ticket -= 1;
        }
    }
}
```

####対策2: Runableを実例化したインスタンスを作成した場合####
Runable実例化したインスタンス一つだけ生成します。そうしたら、マルチスレッドから操作する辺数は唯一なMyRunableオブジェクトに所属することになります。

```java
public class Main {
    public static void main(String[] args) {
        Runnable myRunable = new MyRunable();
        Thread thread1 = new Thread(myRunable);
        Thread thread2 = new Thread(myRunable);
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}


class MyRunable implements Runnable {
    int ticket = 100; // 共通変数
    @Override
    public void run() {
        while (true) {
            if (ticket <= 0) {
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + +ticket);
            ticket -= 1;
        }
    }
}
```

##安全なマルチスレッド##
以上なThreadもしくはRunableの使い方によって、マルチスレッドから共通データを処理することができますが、安全スレッドとは言えません。ではなぜ安全ではないですか

####安全スレッドではない理由####
```sh
thread 2nd:4
thread 1st:3
thread 2nd:2
thread 1st:1
thread 2nd:0　// ここは0が出てきます、安全なスレッドではない証です
```

たとえ新幹線の切符を販売しているときに、０番目の切符を販売できないではないでしょうか？
なぜ0が出たか、理由はこちらです。
![スクリーンショット 2021-05-10 9.48.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/7a632698-9a4c-2f5d-9e87-0176c0268f68.png)

runメソッドの中に、たくさんのスレッドが同時に入ることができます。 それによって、共通のデータが同時に複数のスレッドに触られ、if文、while文など条件に満たして処理に入りましたけど、処理する時もうはや条件に満たせないことが発生してしまい、不安全です。
![スクリーンショット 2021-05-10 9.52.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/22b67f7e-ecfd-45e1-6821-2172498cfb27.png)

####安全スレッド対策####
新幹線に各車両には一つトイレがあります。一つのトイレは同時にもちろん一人しか利用できないです。一つのトイレは同時にもちろん一人しか利用できないことは安全なスレッドになります。
では、新幹線はどうやって、同時に一人しか利用できないことにさせたかというと、理由は二つがあります。

![スクリーンショット 2021-05-10 22.51.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/d459de9b-43f9-395a-d957-4c9714766634.png)
新幹線の便所使用知らせ灯（Photo by PIXTA）

トイレの使用状況を乗客に公開して、利用されるときに、利用しようとする乗客をトイレ外で待たせます。トイレ利用されてないときだけ、利用しようとする乗客にはトイレに入ってもらいます。
図のように、使用状態を知らせ灯によって、乗客はトイレの使用状態に合わせて、トイレに行くタイミングを調整できます。

トイレにドーアにロックを設置します。中にトイレを利用する人にドーア内側からロックをかけてもらいます。そうすると、トイレが利用される時には、いくら外で待っている乗客を入ろうとしても、トイレに入れないです。
![スクリーンショット 2021-05-10 23.01.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b4fd0950-814a-e6a5-e192-092ae4894756.png)
https://chikirin.hatenablog.com/entry/20150610


###安全なスレッドの使い方###
便所使用知らせ灯　+　ロックをかけますの役割を果たせるこはsynchronizedです。
synchronized(ロックオブジェクト)を使えば、新幹線のトイレみたいに安全なスレッドを管理できます。
ここで以下のことと認識していただければ、大丈夫です。
<dl>
<dt>synchronized</dt>
<dd>トイレのドーア</dd>
<dt>ロックオブジェクト</dt>
<dd>ドーアのロック(唯一性が必要)</dd>
</dl>

####ドーアを使います####
######ドーアの使い方1: synchronizedブロック######
安全保護したい部分をsynchronizedブロックで囲みます。

```java
public class Main {
    public static void main(String[] args) {
        Runnable myRunable = new MyRunable();
        Thread thread1 = new Thread(myRunable);
        Thread thread2 = new Thread(myRunable);
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}


class MyRunable implements Runnable {
    int ticket = 100;
    private Object lock = new Object(); // ドーアのロックを作成します

    @Override
    public void run() {
        while (true) {
            synchronized (lock) { // ドーアにロックをかけます
                if (ticket <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + +ticket);
                ticket -= 1;
            }
        }
    }
}
```

気をつけないといけないのは、以下の書き方は安全ではないです

```java
    @Override
    public void run() {
        while (true) {
            synchronized (new Object()) {  // <-　新しいロックを作成して、ドーアにかけます。  だめです、間違います、安全なスレッドではないです。
                if (ticket <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + +ticket);
                ticket -= 1;
            }
        }
    }

```

なぜかというと、毎回スレッドははいると、新しいロックオブジェクトを作成することになります。そうしたら、ロックは唯一ではなく、トイレが利用される時に、外で待ちの乗客を止めることができなくなります

######ドーアの使い方2: synchronizedメソッド######
synchronizedブロックだと、ロックオブジェクトを用意する必要があります。一方、スレッド安全を確保したいコードを別のメソッドにして、メソッドをsynchronizedにすれば、スレッド安全を確保はできます。ロックオブジェクトも自ら用意しなくてもよいです(内部的に処理されてます)。

```java
class MyRunable implements Runnable {
    int ticket = 100;
    @Override
    public void run() {
        while (true) {
            sellTicket();
            if (ticket <= 0) {
                break;
            }
        }
    }

    synchronized private void sellTicket() {
        if (ticket <= 0) {
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + +ticket);
        ticket -= 1;

    }
}
```

####ドーアのロックを使います####
ロックの作りかたは二つがあります

######ドーアのロックの使い方1: new Object() ######
JavaのObject型のオブジェクトを入れる必要がありますので、new Object()でいきます。

######ドーアのロックの使い方2: this ######
MyRunableを元に作られたオブジェクトは一つしか存在していないし、MyRuableもObject側のサブクラスですので、ロックオブジェクトとして使えます。

```java
public class Main {
    public static void main(String[] args) {
        Runnable myRunable = new MyRunable();
        Thread thread1 = new Thread(myRunable);
        Thread thread2 = new Thread(myRunable);
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}


class MyRunable implements Runnable {
    int ticket = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (ticket <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + +ticket);
                ticket -= 1;
            }
        }
    }

}
```

######ドーアのロックの使い方3: MyThread.class ######
クラス名.classもObject型のオブジェクトです、そしてMyThreadは唯一なクラスですので、ロックオブジェクトとして使えます。

```java
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new MyThread();
        Thread thread2 = new MyThread();
        thread1.setName("thread 1st");
        thread2.setName("thread 2nd");
        thread1.start();
        thread2.start();
    }
}

class MyThread extends Thread {
    static int ticket = 100;
    @Override
    public void run() {
        super.run();
        while (true) {
            synchronized (MyThread.class) {
                if (ticket <= 0) {
                    break;
                }

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + +ticket);
                ticket -= 1;
            }
        }
    }
}
```

##応用 Singleton パターン##

###パターン1###
このパタンはなぜかスレッド安全とはいえないかというと、マルチスレッドからgetInstanceアクセスするところで、synchronizedというロックはないから、複数なinstanceが生成されてしまう可能性があります。

```java
class TicketWindowNotSafe{
    static private TicketWindowNotSafe instance = null;
    private static TicketWindowNotSafe getInstance(){
        if (instance == null){
            return new TicketWindowNotSafe();
        }
        return instance;
    }
}
```

###パターン２###
synchronizedというロックを使って、あるスレッドはgetInstanceをアクセスするときに、他のスレッドはgetInstanceのそとで待つことになります。getInstanceは同時に一つのスレッドしかアクセスできないため、スレッド安全とは言えます。
しかし、instanceが生成された場合は、synchronizedの中に入ってからinstanceを取り出すことになります。synchronizedの中に入るためには、前のスレッドがgetInstanceから出ることを待つ必要があります。その待ち時間が長いため、全体的に非効率です。

```java
class TicketWindowSafeNotEfficient{
    static private TicketWindowSafeNotEfficient instance = null;
    private static TicketWindowSafeNotEfficient getInstance(){
        synchronized (TicketWindowSafeNotEfficient.class){
            if (instance != null){
                return instance;
            }
            return new TicketWindowSafeNotEfficient();
        }
    }
}
```

###パラーン３###
synchronizedの外側で、もしinstanceがすでに生成されましたら、ただちにinstanceを取ります。そのため、instanceがすでに生成された場合、synchronizedに入る必要がなく、待ち時間も必要がなく、全体的に効率よくなります。

```java
class TicketWindowSafeAndEfficient{
    static private TicketWindowSafeNotEfficient instance = null;
    private static TicketWindowSafeNotEfficient getInstance(){
        if (instance != null){
            return instance;
        }

        synchronized (TicketWindowSafeNotEfficient.class){
            if (instance != null){
                return instance;
            }
            return new TicketWindowSafeNotEfficient();
        }

    }
}
```

##結論##
Javaのsynchronizedはマルチスレッドを安全に守ることができます。大事な知識です。
決して難しいことではないです。