标签:desc 刷新 static single ide 抽象 java多线程 throws 参考
参考资料:
http://ifeve.com/java-memory-model-4/
http://www.infoq.com/cn/articles/java-memory-model-1
http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
https://en.wikipedia.org/wiki/Singleton_pattern#Java_5_solution
https://www.ibm.com/developerworks/java/library/j-jtp06197/
1. volatile
final class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
以上代码尝试实现单例模式,但存在严重的线程安全风险。Java Memory Model定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。假设Thread1/Thread2并发,instance为它们的共享变量,Thread1与Thread2之间通信必须要经历下面2个步骤:
那么可能的场景之一——Thread1执行完instance = new Singleton(),但刷新到主内存前Thread2的instance == null仍然成立,于是再次执行instance = new Singleton(),这时两个线程得到了两个不同的对象,与预期不符。
final class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
加入锁和双重校验后,仍然存在风险,因为为了提高性能,编译器和处理器常常会对指令做重排序,以Singleton instance = new Singleton()为例,它包含了三个指令:
三个指令执行顺序可能是①②③或①③②,在③执行之后,instance==null将不再成立。可能的场景——假设Thread1/Thread2并发,Thread1执行了除②以外的指令,Thread2的instance==null不成立,虽然得到了内存地址,但由于未调用构造方法而报错。
final class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
为instance变量加上volatile关键字彻底解决问题。volatile的特性:
由于以上特性使volatile极适用于修饰多线程环境下的状态标识。
2. ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
以非线程安全的SimpleDateFormat类为例,在并发运行时会出错,但使用ThreadLocal维护则可以完美避免此问题
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @Description: 测试ThreadLocal */ public class ThreadLocalTest { private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); private static final ThreadLocal<DateFormat> DATE_FORMAT = new ThreadLocal<DateFormat>() { public DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; public static void main(String[] args) throws InterruptedException { String date = "2017-07-06"; testDateFormat(date); testThreadLocal(date); } private static void testDateFormat(String date) throws InterruptedException { multilpleThreadExecute(new Runnable() { @Override public void run() { try { System.out.println(df.parse(date)); } catch (ParseException e) { } } }); } private static void testThreadLocal(String date) throws InterruptedException { multilpleThreadExecute(new Runnable() { @Override public void run() { try { System.out.println(DATE_FORMAT.get().parse(date)); } catch (ParseException e) { } } }); } private static void multilpleThreadExecute(Runnable runnable) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { executorService.execute(runnable); } executorService.shutdown(); executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS); } }
Java多线程系列八——volatile和ThreadLocal
标签:desc 刷新 static single ide 抽象 java多线程 throws 参考
原文地址:http://www.cnblogs.com/hiver/p/7128024.html