技術メモ

技術メモ

ラフなメモ

ThreadLocalを試す

概要

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ThreadLocal.html

このクラスはスレッド・ローカル変数を提供します。これらの変数は、getメソッドまたはsetメソッドを使ってアクセスするスレッドがそれぞれ独自に、変数の初期化されたコピーを持つという点で、通常の変数と異なります。通常、ThreadLocalインスタンスは、状態をスレッドに関連付けようとするクラスでのprivate staticフィールドです(ユーザーID、トランザクションIDなど)。

ThreadLocal変数とは、同一スレッドのみ読み書きできる変数のことです。スレッドセーフにデータを利用することがうれしいです。

使い所

  • スレッド・セーフでないオブジェクト全体のカプセル化
  • ThreadLocalにオブジェクトのスレッド固有の状態をカプセル化することで、ステートフルなSingletonや共用オブジェクトをスレッド・セーフにする場合
  • スレッドごとのコンテキストのロギング

サンプル

MyThreadLocal.java

import java.util.Date;
import java.util.UUID;

public class MyThreadLocal {

    public static class MyRunnable implements Runnable {

        private ThreadLocal<String> threadLocal = new ThreadLocal<>();

        @Override
        public void run() {

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), "start");
            threadLocal.set(UUID.randomUUID().toString());

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), threadLocal.get());

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), "stop");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable task = new MyRunnable();

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(task);
            thread.start();
        }
    }
}

結果
スレッドごとに独立したUUIDを保持していることが分かります。

Fri May 31 20:52:57 JST 2019 [Thread-9] :start
Fri May 31 20:52:57 JST 2019 [Thread-8] :start
Fri May 31 20:52:57 JST 2019 [Thread-7] :start
Fri May 31 20:52:57 JST 2019 [Thread-1] :start
Fri May 31 20:52:57 JST 2019 [Thread-6] :start
Fri May 31 20:52:57 JST 2019 [Thread-2] :start
Fri May 31 20:52:57 JST 2019 [Thread-5] :start
Fri May 31 20:52:57 JST 2019 [Thread-3] :start
Fri May 31 20:52:57 JST 2019 [Thread-4] :start
Fri May 31 20:52:57 JST 2019 [Thread-0] :start
Fri May 31 20:53:00 JST 2019 [Thread-8] :f7dec58f-abae-4f33-a2e8-3b3385c97e69
Fri May 31 20:53:00 JST 2019 [Thread-3] :5a94e475-81ad-4d68-95ad-4866137512a0
Fri May 31 20:53:00 JST 2019 [Thread-5] :c5aad3c5-7eef-4c22-98ea-9b21023ce7e7
Fri May 31 20:53:00 JST 2019 [Thread-4] :5d415711-a20e-40b3-a855-35e05710c619
Fri May 31 20:53:00 JST 2019 [Thread-0] :5b3bf5f5-f6d4-4766-8523-54031d4279e0
Fri May 31 20:53:00 JST 2019 [Thread-2] :92e1c2b6-7a2d-4095-9efa-e7108ae1baa1
Fri May 31 20:53:00 JST 2019 [Thread-1] :e2742667-ff5c-4358-9a06-6d4cd2c2c27f
Fri May 31 20:53:00 JST 2019 [Thread-6] :c3548d1d-fdf4-4704-9ee3-b52d1d1c53fb
Fri May 31 20:53:00 JST 2019 [Thread-7] :ebdc4e64-922d-4493-896f-aa0a68a9149f
Fri May 31 20:53:00 JST 2019 [Thread-9] :ffc661bc-5562-41c9-bed6-44045377be3e
Fri May 31 20:53:02 JST 2019 [Thread-8] :stop
Fri May 31 20:53:02 JST 2019 [Thread-3] :stop
Fri May 31 20:53:02 JST 2019 [Thread-5] :stop
Fri May 31 20:53:02 JST 2019 [Thread-4] :stop
Fri May 31 20:53:02 JST 2019 [Thread-0] :stop
Fri May 31 20:53:02 JST 2019 [Thread-2] :stop
Fri May 31 20:53:02 JST 2019 [Thread-1] :stop
Fri May 31 20:53:02 JST 2019 [Thread-6] :stop
Fri May 31 20:53:02 JST 2019 [Thread-7] :stop
Fri May 31 20:53:02 JST 2019 [Thread-9] :stop

NGの例。以下のコードはスレッドセーフでなく、スレッド内で保持した変数が上書きされているのが分かります。

MyNonThreadLocal.java

import java.util.Date;
import java.util.UUID;

public class MyNonThreadLocal {

    public static class MyRunnable implements Runnable {

        private String threadLocal = null;

        @Override
        public void run() {

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), "start");
            threadLocal = UUID.randomUUID().toString();

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), threadLocal);

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.printf("%s [%s] :%s\n", new Date(), Thread.currentThread().getName(), "stop");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable task = new MyRunnable();

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(task);
            thread.start();
        }
    }
}

結果

Fri May 31 21:01:07 JST 2019 [Thread-0] :start
Fri May 31 21:01:07 JST 2019 [Thread-5] :start
Fri May 31 21:01:07 JST 2019 [Thread-3] :start
Fri May 31 21:01:07 JST 2019 [Thread-6] :start
Fri May 31 21:01:07 JST 2019 [Thread-1] :start
Fri May 31 21:01:07 JST 2019 [Thread-2] :start
Fri May 31 21:01:07 JST 2019 [Thread-4] :start
Fri May 31 21:01:07 JST 2019 [Thread-8] :start
Fri May 31 21:01:07 JST 2019 [Thread-9] :start
Fri May 31 21:01:07 JST 2019 [Thread-7] :start
Fri May 31 21:01:09 JST 2019 [Thread-3] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-2] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-9] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-1] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-7] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-8] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-4] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-6] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-0] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:09 JST 2019 [Thread-5] :43e2ebc5-742f-49c8-8b0a-a1d3562a466a
Fri May 31 21:01:11 JST 2019 [Thread-3] :stop
Fri May 31 21:01:11 JST 2019 [Thread-2] :stop
Fri May 31 21:01:11 JST 2019 [Thread-9] :stop
Fri May 31 21:01:11 JST 2019 [Thread-1] :stop
Fri May 31 21:01:11 JST 2019 [Thread-7] :stop
Fri May 31 21:01:11 JST 2019 [Thread-8] :stop
Fri May 31 21:01:11 JST 2019 [Thread-4] :stop
Fri May 31 21:01:11 JST 2019 [Thread-6] :stop
Fri May 31 21:01:11 JST 2019 [Thread-0] :stop
Fri May 31 21:01:11 JST 2019 [Thread-5] :stop

参考