【Java多线程系列八】volatile和ThreadLocal
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刷新到主内存中去
- Thread2到主内存中去读取Thread1之前已更新过的instance
那么可能的场景之一——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分配内存
- ②调用Singleton构造方法
- ③把instance指向分配的内存地址
三个指令执行顺序可能是①②③或①③②,在③执行之后,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的变量修改后将立即刷新到主内存,其他线程即可读取到新值
- 编译器利用内存屏障的概念禁止上述三条指令的重排序,只允许①②③的执行顺序
由于以上特性使volatile极适用于修饰多线程环境下的状态标识。
2. ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
以非线程安全的SimpleDateFormat类为例,在并发运行时会出错,但使用ThreadLocal维护则可以完美避免此问题
package com.concurrent.test; import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; 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(final String date) throws InterruptedException {
multilpleThreadExecute(new Runnable() {
@Override
public void run() {
try {
System.out.println(df.parse(date));
} catch (Exception e) {
e.printStackTrace();
}
}
});
} private static void testThreadLocal(final String date) throws InterruptedException {
multilpleThreadExecute(new Runnable() {
@Override
public void run() {
try {
System.out.println(DATE_FORMAT.get().parse(date));
} catch (Exception e) {
e.printStackTrace();
}
}
});
} 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的更多相关文章
- Java多线程系列八——volatile和ThreadLocal
参考资料: http://ifeve.com/java-memory-model-4/ http://www.infoq.com/cn/articles/java-memory-model-1 htt ...
- java多线程系列(八)---CountDownLatch和CyclicBarrie
CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...
- java多线程系列(九)---ArrayBlockingQueue源码分析
java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java多线程系列--“JUC锁”08之 共享锁和ReentrantReadWriteLock
概要 Java的JUC(java.util.concurrent)包中的锁包括"独占锁"和"共享锁".在“Java多线程系列--“JUC锁”02之 互斥锁Ree ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- java多线程系列(三)---等待通知机制
等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...
- Java多线程系列——从菜鸟到入门
持续更新系列. 参考自Java多线程系列目录(共43篇).<Java并发编程实战>.<实战Java高并发程序设计>.<Java并发编程的艺术>. 基础 Java多线 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
随机推荐
- Selenium:下拉框处理(Select模块)
在UI自动化测试过程中,经常会遇到一些下拉框,如果我们基于Webdriver操作的话就需要click两次,而且很容易出现问题,实际上Selenium给我们提供了专门的Select(下拉框处理模块). ...
- 路由参数 query和params
1. path:'www.baidu.com' query { id:122 } 对应地址:http:'www.baidu.coom?id=122' 类似get方式 2.name:'baidu' ...
- 大众点评Java开发实时应用监控平台-CAT
CAT介绍 CAT是基于Java开发的实时应用监控平台,包括实时应用监控,业务监控. CAT作为大众点评网基础监控组件,它已经在中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等)中得到广泛应 ...
- jQuery对于demo元素的上移、下移、删除操作等实现
今天给大家分享一个实用的jQuery技能:dom元素的操作:我们经常会去获取dom元素去实现交互效果,以及数据的操作. 首先复习一下jQuery DOM 元素方法: .get() 获得由选择器指定的D ...
- maven私服的项目使用配置
环境: eclipse .maven.nexus. 1.配置setting.xml文件 1.1.配置本地仓库位置:文件中,存在节点 “localRepository”,默认是注释,也就是本地仓库使用 ...
- Python基础篇(初始函数)
Python初始函数: 一.什么是函数 1.我们到目前为止, 已经可以完成一些软件的基础功能了. 那么我们来完成这样一个功 能: 约x: print("拿出手机") print(& ...
- IPv6 关于路由器配置静态IPv6路由的命令
今天在学习路由器配置ipv6 的时候遇到了一点疑惑 一条命令为:ipv6 route FE80:0202::/32 serail 0/1 201 一条命令为:ipv6 route FE80:0202: ...
- 第6篇如何访问pod
一.通过 Service 访问 Pod: 我们不应该期望 Kubernetes Pod 是健壮的,而是要假设 Pod 中的容器很可能因为各种原因发生故障而死掉.Deployment 等 con ...
- WPF ComboBox 默认选中无效
在WPF开发当中,我发现ComboBox的默认选中逻辑失效了,仔细查找后发现后台逻辑并没有出现问题. 测试后发现在XAML中,ComBoBox控件的SelectedValue属性需要写在ItemSou ...
- MQ总结
对于ActiveMQ:JMS可靠消息机制.JMS发布订阅与点对点通讯.ActiveMQ高可用集群解决方案.ActiveMQ补偿与重试策略.ActiveMQ整合SpringBoot Kafka:整合Zo ...