java何でもメモ2

自分の勉強用のメモ。

# メモリ領域
変数の値、生成されたインスタンス、呼び出されたメソッドの履歴などを格納する領域。
これらをどこに格納するかは、JVMによって管理している。
メモリ領域には2種類あり

- スタック
- ヒープ

がある。

## スタック
メソッドの呼び出し履歴、メソッド内で宣言された変数（ローカル変数）の値。
メソッドの処理が終わると、その情報はスタックから削除される。

## ヒープ
クラスから生成されたインスタンス。
インスタンスが、どの変数からも参照されなくなったとき（参照先の変更、nullの代入）
に、JVMのガーベッジコレクションで自動で開放される。



# スレッド

以下の２種類がある。

- シングルスレッド
- マルチスレッド

## マルチスレッドを作成する
方法は２つ

- Threadクラスを継承した新しいクラスを作る
- Runnableインターフェースを実装した新しいクラスを作る。

### Threadクラスで作る

Threadクラスを継承したクラスのインスタンスを生成して、
そのインスタンスのstartメソッドを呼ぶ。

実行するのは、startメソッドだけど、
Threadクラスを継承したサブクラスでは、
Threadクラスが持つrunメソッドをオーバライドする。

startメソッドを経由して、runメソッドが実行される。
結果は必ず同じではなく、実行されるたびに変わる（変わらない時もある）

```java
class MyTread extends Thread{
    //メモ：スーパークラスで定義されているアクセス修飾子より、レベルを下げることはできない
    //Thread から継承されたメソッドの可視性を下げることはできません
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("MyTread.run:"+i);
        }
    }
}

public class SampleProgram{
    public static void main(String[] args) {
        MyTread mt = new MyTread();
        mt.start();

        for(int i = 0; i < 5; i++) {
            System.out.println("main:"+i);
        }
    }
}

//結果
main:0
main:1
main:2
MyTread.run:0
MyTread.run:1
MyTread.run:2
MyTread.run:3
main:3
MyTread.run:4
main:4

```

### Runnableインタフェースで作る

継承して作った時とあんまり変わらない。

```java
class MyTread implements Runnable{
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("MyTread.run:"+i);
        }
    }
}

public class SampleProgram{
    public static void main(String[] args) {
        MyTread mt = new MyTread();
        Thread t = new Thread(mt);
        t.start();

        for(int i = 0; i < 5; i++) {
            System.out.println("main:"+i);
        }
    }
}

```


## Thread.sleep(ミリ秒(=1000分の１秒))

スレッドの処理を一定時間止める。
単位は、ミリ秒。
1000：1秒
2000：2秒

マイクロ秒だと
1000000：1秒

Thread.sleepメソッドは、InterruptedException型の例外を投げる可能性があるので
catchしないといけない。

```java
public class SampleProgram{
    public static void main(String[] args) {

        for(int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("main:"+i);
        }
    }
}

// Runnableインターフェースを実装したほうで試してみた
class MyTread implements Runnable {
    public void run() {
        for(int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
            System.out.println("MyTread.run:"+i);
        }
    }
}

public class SampleProgram{
    public static void main(String[] args) {
        MyTread mt = new MyTread();
        Thread t = new Thread(mt);
        t.start();

        for(int i = 0; i < 5; i++) {
            System.out.println("main:"+i);
        }
    }
}
```

## join()

スレッドの処理を待つことができる。

joinメソッドは、InterruptedException型の例外を投げる可能性があるので
catchしないといけない。

MyTreadクラスの処理がすべて終わってから、mainの処理が実行されている。


```java
class MyTread implements Runnable {
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("MyTread.run:"+i);
        }
    }
}

public class SampleProgram{
    //今度は、throwsしてみる。
    public static void main(String[] args) throws InterruptedException {
        MyTread mt = new MyTread();
        Thread t = new Thread(mt);
        t.start();

        t.join();
        for(int i = 0; i < 5; i++) {
            System.out.println("main:"+i);
        }
    }
}

```

## スレッドを止める

runメソッドの処理を終了させれば、そのスレッドの処理は終了する。
うーん、、、、今は置いておこう。

## 同期をとる。

マルチスレッドで動く場合、複数のスレッドで同じ変数の値を共有すると
不整合が生じることがある。

以下は、合計金額が1000000円になる予定のプログラム

```java
class Bank{
    static int money = 0;
    static void addOneYen() {
        money++;
    }
}
class Customer extends Thread{
    public void run() {
        for(int i = 0; i < 10000; i++) {
            Bank.addOneYen();
        }
    }
}
public class MultiThreadEx {
    public static void main(String[] args) {
        Customer[] customers = new Customer[100];
        for(int i = 0; i < 100; i++) {
            customers[i] = new Customer();
            customers[i].start();
        }

        for(int i = 0; i < 100; i++) {
            try {
                customers[i].join();
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Bank.money["+Bank.money+"]");
    }
}

//結果
Bank.money[628887]
```

現在の金額を参照・更新するタイミングで、各スレット間で整合性がとれないため、
合計金額がおかしくなる。
そんな時は、addOneYenメソッドの前に「synchronized」を付ける。

```java
    static synchronized void addOneYen() {
        money++;
    }
````
synchronizedをメソッドの前につけると、1度に1つのスレッドしか実行できない。
あるスレットが処理を行っているときは、ほかのスレットは自分の番まで待機する。