【Java】マルチスレッド処理


結論

  • プロセスとスレッドの違いは重要
  • マルチスレッドの意義は独立に動かし、かつ同じプロセス内のメモリ空間を共有することができること

スレッドとは

  • プログラムを実行する処理の最小単位
  • アプリはメインスレッドという単一スレッドで動く
  • しかし単一スレッドでは、ネットワーク通信時などで、アプリ利用者は次の操作を通信終了まで待たないといけなくなる
  • スレッドが複数ある(=マルチスレッド)と、
    ネットワーク通信はバックグラウンドで他のスレッドが実行し、通信中でもメインスレッドで処理を並行して実行することができる

プロセスとは

  • プログラムのインスタンスそのもの
  • プロセス起動にはCPUとメモリの割り当てが必要
  • プロセスは1つ以上のスレッドを持つ
    • 1対1..n(1以上のn)関係
  • UML表記では、プロセス側から見てスレッドは 1…n 個

 プロセス 1
   |
 スレッド 1..n

メモリ構成

  • プロセス:Main関数で動いているプログラム
  • プロセスという大きい箱のなかにスレッドがある
    • Main関数は1スレッドでうごいている
    • あるプロセスは1個以上のスレッドを持たないといけない(mainスレッド

スレッドの特徴 - 独立・メモリ空間共有

  • スレッド同士はお互い独立して動く
    • 1個止まってもバックグラウンドで動くスレッドは独立して稼働
    • メインスレッドで通信処理をしていると通信して答えが返ってくる間止まってしまうので、
その間も処理させるために並列処理させる
  • マルチスレッドで3スレッド立ち上げたときに1プロセス内にある理由はメモリ空間を共有するため
    • Mainスレッド内に変数を定義したら全スレッドがアクセスできる
public class Main {
    String hoge = "hoge"; //この変数は共有される
    public static void main(String[] args) {
        new Main().run();
    }
    private void run(){
        while (true) {
            System.out.println("1"); //Mainスレッド
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("2");
        }
    }
}

スレッド生成・実行

  • Threadクラスを継承
  • Runnableインターフェース実装

Threadクラスを継承

  • Thread派生クラスでは絶対runメソッド(呼び出すエンドポイント)をオーバーライドする
  • Thread派生クラスのメソッド
    • currentThread:実行中スレッド取得
    • getId:スレッドID取得
    • isAlive:スレッド生存確認
    • join:スレッド終了するまでms待機
    • setPriority:優先順位設定
    • sleep:スレッド実行休止
public class MyThread extends Thread {
//スレッド実処理はThread派生クラス
  @Override
  public void run() {
    for (var i = 0; i < 30; i++) {
      //getNameでスレッド名取得
      System.out.println(this.getName() + ": " + i);
    }
  }
}
public class ThreadBasic {

  public static void main(String[] args) {
    //インスタンス化しスレッド生成
    var th1 = new MyThread();
    var th2 = new MyThread();
    var th3 = new MyThread();
    //startメソッドでスレッド開始
    th1.start();
    th2.start();
    th3.start();
  }
}

Runnableインターフェース実装

  • 継承ではなく実装(implements Runnable)
    • 関数型インターフェースである
  • スレッド名に直接アクセスできないのでThread.currentThread()静的メソッドで現スレッドを取得し、getNameメソッドにアクセス
  • インスタンス化にはThreadコンストラクターにRunnable実装クラスのインスタンスを渡す
public class MyRunner implements Runnable {
  //スレッド実処理
  @Override
  public void run() {
    for (var i = 0; i < 30; i++) {
      System.out.println(Thread.currentThread().getName() + ": " + i);
    }
  }
}
public class RunnableBasic {

  public static void main(String[] args) {
    //スレッド生成
    var th1 = new Thread(new MyRunner());
    var th2 = new Thread(new MyRunner());
    var th3 = new Thread(new MyRunner());
    //スレッド開始
    th1.start();
    th2.start();
    th3.start();
  }
}