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