Java ThreadLocal类容许您建立只能由同一线程读写的变量。所以,即便两个线程正在执行相同的代码,而且代码引用了相同的ThreadLocal变量,这两个线程也不能看到彼此的ThreadLocal变量。所以,Java ThreadLocal类提供了一种使代码线程安全的简单方法。java
//建立 private ThreadLocal threadLocal = new ThreadLocal(); //一旦建立了ThreadLocal,就能够使用它的set()方法设置要存储在其中的值。 threadLocal.set("A thread local value"); //获取值 String threadLocalValue = (String) threadLocal.get(); //移除一个值 threadLocal.remove();
//您能够建立具备泛型类型的ThreadLocal。使用泛型类型时,只能将泛型类型的对象设置为ThreadLocal的值。 //此外,您没必要对get()返回的值进行类型转换。 private ThreadLocal<String> myThreadLocal = new ThreadLocal<String>(); //如今只能在ThreadLocal实例中存储字符串。另外,您不须要对从ThreadLocal得到的值进行类型转换 myThreadLocal.set("Hello ThreadLocal"); String threadLocalValue = myThreadLocal.get();
能够为Java ThreadLocal设置一个初始值,该值将在第一次调用get()时使用.
有两种方式指定ThreadLocal初始值:安全
//为Java ThreadLocal变量指定初始值的第一种方法是建立ThreadLocal的子类,该子类覆盖了它的initialValue()方法。 //建立ThreadLocal子类的最简单方法是直接在建立ThreadLocal变量的地方建立一个匿名子类。下面是建匿名子类的示例, //该子类覆盖了initialValue()方法 private ThreadLocal myThreadLocal = new ThreadLocal<String>() { @Override protected String initialValue() { return String.valueOf(System.currentTimeMillis()); } }; //注意,不一样的线程仍然会看到不一样的初始值。每一个线程将建立本身的初始值。 //只有当从initialValue()方法返回彻底相同的对象时,全部线程才会看到相同的对象。 //可是,首先使用ThreadLocal的所有意义在于避免不一样的线程看到相同的实例
//为Java ThreadLocal变量指定初始值的第二种方法是使用其内部的静态工厂方法(Supplier),将Supplier接口实现做为参数传递。 //这个Supplier实现为ThreadLocal提供初始值。 //下面是一个使用其withInitial()静态工厂方法建立ThreadLocal的示例,该方法传递一个简单的供应商实现做为参数 ThreadLocal<String> threadLocal = ThreadLocal.withInitial(new Supplier<String>() { @Override public String get() { return String.valueOf(System.currentTimeMillis()); } }); //Java8 lambda表达式的写法 ThreadLocal threadLocal = ThreadLocal.withInitial( () -> { return String.valueOf(System.currentTimeMillis()); } ); //还能够更短 ThreadLocal threadLocal3 = ThreadLocal.withInitial( () -> String.valueOf(System.currentTimeMillis()) );
//在某些状况下,您不能使用设置初始值的标准方法。例如,您可能须要一些在建立ThreadLocal变量时不可用的配置信息。 //在这种状况下,能够延迟设置初始值。 //下面是如何在Java ThreadLocal上惰性地设置初始值的示例 public class MyDateFormatter { private ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>(); public String format(Date date) { SimpleDateFormat simpleDateFormat = getThreadLocalSimpleDateFormat(); return simpleDateFormat.format(date); } private SimpleDateFormat getThreadLocalSimpleDateFormat() { SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get(); if(simpleDateFormat == null) { simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); simpleDateFormatThreadLocal.set(simpleDateFormat); } return simpleDateFormat; } } //注意format()方法是如何调用getThreadLocalSimpleDateFormat()方法来获取Java SimpleDatFormat实例的。 //若是在ThreadLocal中没有设置SimpleDateFormat实例,那么就会在ThreadLocal变量中建立并设置一个新的SimpleDateFormat。 //一旦线程在ThreadLocal变量中设置了本身的SimpleDateFormat,就会对该线程使用相同的SimpleDateFormat对象继续前进。 //但只是为了那条线。每一个线程建立本身的SimpleDateFormat实例,由于它们不能看到在ThreadLocal变量上设置的其余实例。 //SimpleDateFormat类不是线程安全的,所以多个线程不能同时使用它。为了解决这个问题, //上面的MyDateFormatter类为每一个线程建立一个SimpleDateFormat, //所以调用format()方法的每一个线程将使用本身的SimpleDateFormat实例。
若是您计划从传递给Java线程池或Java ExecutorService的任务内部使用Java ThreadLocal,请记住,您不能保证哪一个线程将执行您的任务。
可是,若是您所须要的只是确保每一个线程使用某个对象的本身的实例,那么这不是问题。而后,您能够将Java ThreadLocal与线程池或ExecutorService一块儿使用。多线程
public class ThreadLocalExample { public static void main(String[] args) { MyRunnable sharedRunnableInstance = new MyRunnable(); Thread thread1 = new Thread(sharedRunnableInstance); Thread thread2 = new Thread(sharedRunnableInstance); thread1.start(); thread2.start(); thread1.join(); //wait for thread 1 to terminate thread2.join(); //wait for thread 2 to terminate } } // public class MyRunnable implements Runnable { private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); @Override public void run() { threadLocal.set( (int) (Math.random() * 100D) ); try { Thread.sleep(2000); } catch (InterruptedException e) { } System.out.println(threadLocal.get()); } } //这个例子建立了一个MyRunnable实例, //它被传递给两个不一样的线程。两个线程都执行run()方法,所以在ThreadLocal实例上设置不一样的值。 //若是对set()调用的访问是同步的,而且它不是ThreadLocal对象,那么第二个线程就会覆盖第一个线程设置的值。 //可是,由于它是ThreadLocal对象,因此两个线程不能看到彼此的值。所以,它们设置和获取不一样的值。
InheritableThreadLocal类是ThreadLocal的子类。与每一个线程在ThreadLocal中都有本身的值不一样,
InheritableThreadLocal将对值的访问权授予一个线程和该线程建立的全部子线程。下面是一个完整的Java InheritableThreadLocal示例:dom
public class InheritableThreadLocalBasicExample { public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal<>(); InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>(); Thread thread1 = new Thread(() -> { System.out.println("===== Thread 1 ====="); threadLocal.set("Thread 1 - ThreadLocal"); inheritableThreadLocal.set("Thread 1 - InheritableThreadLocal"); System.out.println(threadLocal.get()); System.out.println(inheritableThreadLocal.get()); Thread childThread = new Thread( () -> { System.out.println("===== ChildThread ====="); System.out.println(threadLocal.get()); System.out.println(inheritableThreadLocal.get()); }); childThread.start(); }); thread1.start(); Thread thread2 = new Thread(() -> { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===== Thread2 ====="); System.out.println(threadLocal.get()); System.out.println(inheritableThreadLocal.get()); }); thread2.start(); } } //这个例子建立了一个普通的Java ThreadLocal和一个Java InheritableThreadLocal。 //而后,示例建立一个线程,该线程设置ThreadLocal和InheritableThreadLocal的值——而后建立一个子线程, //该子线程访问ThreadLocal和InheritableThreadLocal的值。只有InheritableThreadLocal的值对子线程是可见的。 //最后,示例建立了第三个线程,该线程也尝试访问ThreadLocal和InheritableThreadLocal——可是它看不到第一个线程存储的任何值 //输出值: ===== Thread 1 ===== Thread 1 - ThreadLocal Thread 1 - InheritableThreadLocal ===== ChildThread ===== null Thread 1 - InheritableThreadLocal ===== Thread2 ===== null null