- 浏览: 518973 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (161)
- 多线程与并发编程 (20)
- 算法和数据结构 (8)
- 缓存 (0)
- HttpClient (2)
- 疑难杂症 (11)
- Java内存管理 (17)
- 分布式开发 (14)
- Linux常用命令 (10)
- OSGI (0)
- UML (2)
- 趣味面试题 (5)
- 设计模式 (8)
- Java类加载 (2)
- JSTL (1)
- Web 服务器 (4)
- IT人生 (3)
- Java基础 (11)
- Flash技术 (7)
- 新知识 (3)
- 常用速备速查 (4)
- 版本控制 (1)
- Java集合工具类 (6)
- web前端技术 (1)
- 趣味话题 (1)
- 安全 (0)
- 架构设计 (5)
- Spring (4)
- 负载均衡技术 (2)
- 持久层技术 (2)
- MySQL单机多实例方案 (1)
- 收藏备用 (0)
- 性能优化 (3)
最新评论
-
liuwuhen:
...
Pushlet的工作原理 -
fbwfbi:
fengchuizhuming 写道楼主的完全正确。鉴定完毕楼 ...
硬件同步原语(CAS)理论 -
passerby_whu:
uule 写道这个测试后结果为:“testPageConten ...
FutureTask的使用方法和使用实例 -
fengchuizhuming:
楼主的完全正确。鉴定完毕
硬件同步原语(CAS)理论 -
edwardjuice:
FutureTask的使用方法和使用实例
对于多线程编程来说,同步问题是我们需要考虑的最多的问题,同步的锁什么时候加,加在哪里都需要考虑,当然在不影响功能的情况下,同步越少越好,锁加的越迟越优是我们都必须认同的。DCL(Double Check Lock)就是为了达到这个目的。
DCL简单来说就是check-lock-check-act,先检查再锁,锁之后再检查一次,最后才执行操作。这样做的目的是尽可能的推迟锁的时间。网上普遍举的一个例子是延迟加载的例子。
- public class LazySingleton {
- private static volatile LazySingleton instance;
- public static LazySingleton getInstantce() {
- if (instance == null ) {
- synchronized (LazySingleton. class ) {
- if (instance == null ) {
- instance = new LazySingleton();
- }
- }
- }
- return instance;
- }
- }
对上面的例子来说,我们当然也可以把锁加载方法上,那样的话每次获取实例都需
要获取锁,但其实对这个instance来说,只有在第一次创建实例的时候才需要同步,所以为了减少同步,我们先check了一下,看看这个
instance是否为空,如果为空,表示是第一使用这个instance,那就锁住它,new一个LazySingleton的实例,下次另一个线程来
getInstance的时候,看到这个instance不为空,就表示已经创建过一个实例了,那就可以直接得到这个实例,避免再次锁。这是第一个
check的作用。
第二个check是解决锁竞争情况下的问题,假设现在两个线程来请求getInstance,A、B线程同时发现instance为空,因为我们
在方法上没有加锁,然后A线程率先获得锁,进入同步代码块,new了一个instance,之后释放锁,接着B线程获得了这个锁,发现instance已
经被创建了,就直接释放锁,退出同步代码块。所以这就是check-lock-then check。
网上有很多文章讨论DCL的失效问题,我就不赘述了,Java5之后可以通过将字段声明为volatile来避免这个问题。
我推荐一篇很好的文章《用happen-before规则重新审视DCL》,里面讲的非常好。
上面这个是最简单的例子,网上随处可见,双重检查的使用可不只限于单例的初始化,下面我举个实际使用中的例子。
缓存用户信息,我们用一个hashmap做用户信息的缓存,key是userId。
- public class UserCacheDBService {
- private volatile Map<Long, UserDO> map = new ConcurrentHashMap<Long, UserDO>();
- private Object mutex = new Object();
- /**
- * 取用户数据,先从缓存中取,缓存中没有再从DB取
- * @param userId
- * @return
- */
- public UserDO getUserDO(Long userId) {
- UserDO userDO = map.get(userId);
- if (userDO == null ) { ① check
- synchronized (mutex) { ② lock
- if (!map.containsKey(userId)) { ③ check
- userDO = getUserFromDB(userId); ④ act
- map.put(userId, userDO);
- }
- }
- }
- if (userDO == null ) { ⑤
- userDO = map.get(userId);
- }
- return userDO;
- }
- private UserDO getUserFromDB(Long userId) {
- // TODO Auto-generated method stub
- return null ;
- }
- }
public class UserCacheDBService { private volatile Map<Long, UserDO> map = new ConcurrentHashMap<Long, UserDO>(); private Object mutex = new Object(); /** * 取用户数据,先从缓存中取,缓存中没有再从DB取 * @param userId * @return */ public UserDO getUserDO(Long userId) { UserDO userDO = map.get(userId); if(userDO == null) { ① check synchronized(mutex) { ② lock if (!map.containsKey(userId)) { ③ check userDO = getUserFromDB(userId); ④ act map.put(userId, userDO); } } } if(userDO == null) { ⑤ userDO = map.get(userId); } return userDO; } private UserDO getUserFromDB(Long userId) { // TODO Auto-generated method stub return null; } }
三种做法:
1、
没有锁,即没有②和③,当在代码①处判断userDO为空之后,直接从DB取数据,这种情况下有可能会造成数据的错误。举个例子,A和B两个线程,A线程
需要取用户信息,B线程更新这个user,同时把更新后的数据放入map。在没有任何锁的情况下,A线程在时间上先于B线程,A首先从DB取出这个
user,随后线程调度,B线程更新了user,并把新的user放入map,最后A再把自己之前得到的老的user放入map,就覆盖了B的操作。B以
为自己已经更新了缓存,其实并没有。
2、
没有第二次check,即没有③的情况,在加锁之后立即从DB取数据,这种情况可能会多几次DB操作。同样A和B两个线程,都需要取用户信息,A和B在进
入代码①处时都发现map中没有自己需要的user,随后A线程率先获得锁,把新user放入map,释放锁,紧接着B线程获得锁,又从DB取了一次数据
放入map。
3、
双重检查,取用户数据的时候,我们首先从map中根据userId获取UserDO,然后check是否取到了user(即user是否为空),如果没有
取到,那就开始lock,然后再check一次map中是否有这个user信息(避免其他线程先获得锁,已经往map中放了这个user),没有的话,从
DB中得到user,放入map。
4、 在⑤处又判断了一次userDO为空的话就从map中取一次,这是由于此线程有可能在代码③处发现map中已经存在这个userDO,就没有进行④操作。
所以DCL只要记住:check-lock-check-act!
评论
public class Foo {
private static class FooHolder {
static final Foo foo = new Foo();
}
public static Foo getFoo() {
return FooHolder.foo;
}
}
发表评论
-
死锁实例
2011-05-19 14:21 1903下面这道题,是考死锁的,比较简单,想两个问题: 1.什么时候 ... -
Java存储模型
2011-05-18 13:29 01.什么是存储模型 没有适当的同步,编译器生成指令的次序,可 ... -
设计模式-组合模式
2011-05-16 15:48 1072组合模式的定义: 将对象组合成树的形式来表示整体和局部之 ... -
CompleteService介绍和使用实例
2011-05-11 17:31 3866当向Executor提交批处理任务时,并且希望在它们完成后获得 ... -
CyclicBarrier的使用实例
2011-05-11 15:45 1438CyclicBarrier允许给定数量的线程全部到达关卡点时, ... -
CopyOnWriteArrayList工作原理和实例
2011-05-05 23:43 3340CopyOnWriteArrayList顾名思义,在写入操作时 ... -
Semaphore的介绍和使用实例
2011-04-27 22:32 2768Semaphore可以用来控制能 ... -
FutureTask的使用方法和使用实例
2011-04-27 15:34 13253FutureTask是一种可以取消的异步的计算任务。它的计算是 ... -
CountDownLatch的使用实例
2011-04-26 22:20 8182CountDownLatch CountDownl ... -
Java 并发编程基础-共享对象
2011-04-19 14:48 1406Java 并发编程基础 ... -
从JVM并发看CPU内存指令重排序(Memory Reordering)
2011-04-18 16:17 1445我们都知道,现在的计算机, cpu 在计算的时候 ... -
Java并发编程基础
2011-04-15 14:55 1512Java 并发编程基 ... -
Java多线程基础
2011-04-13 15:52 4698Java 多线程基础 ... 2 ... -
硬件同步原语(CAS)理论
2010-09-24 22:13 6948在Java并发编程中,常常 ... -
java5中使用interrupt()来停止java线程的方法(转)
2010-08-18 23:24 3058在开发java多线程时,如果要停止线程这个问题很头痛吧,不过在 ... -
并发访问的问题解决方案
2010-07-26 18:15 1965目前正在做基于Red 5 的Meeting系统,我们会在Mee ... -
用并发包中的重入锁实现生产消费模型
2010-06-15 00:07 1397传统的生产消费模型,实际上是通过一个条件来调节生产者和消费者线 ... -
ThreadLocal原理(转)
2010-03-24 18:06 2126http://jzhua.iteye.com/blog/517 ... -
(转)Java偏向锁实现原理(Biased Locking)
2010-03-21 22:24 1325http://www.iteye.com/topic/5180 ... -
生产消费模型实例
2010-03-02 23:23 1674“生产者-消费者-仓储”模型,包含三种角色: 1.生产者 ...
相关推荐
主要为大家详细介绍了Java中的双重检查(Double-Check),感兴趣的小伙伴们可以参考一下
JAVA转byte[]为int,long,double
java代码-double check单例模式
使用Java,double 进行运算时,经常出现精度丢失的问题,总是在一个正确的结果左右偏0.0000**1。 特别在实际项目中,通过一个公式校验该值是否大于0,如果大于0我们会做一件事情,小于0我们又处理其他事情。 这样的...
使用java语言实现16位内存数据转化为double型
Java Double相加出现的怪事,浮点运算不精确问题
java实现任意进制的转换,包括2进制转16进制,10进制转16进制,10进制转任意进制,36进制加法,可自行手动设置转换进制和需要转换的数字.
Java中的简单浮点数类型float和double不能够进行运算,因为大多数情况下是正常的,但是偶尔会出现如上所示的问题。这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制...
本资源整理了几种c++中double与string相互转换方式,包含完整的vs2010工程。可直接使用。
java\String类型转换成整数double
java中数据类型转换 收藏 java中数据类型转换 1如何将字串 String 转换成整数 int? A.... 1)....i = Integer.parseInt([String],[int radix]);...注: 字串转成 Double, Float, Long 的方法大同小异.
string转换double string转换double string转换double string转换double
Java Double 类型比较大小 不能直接使用 >0
2.首先来两个int类型的数据(或double型): 4.将int型(double型)转换为QByteArray型: 5.QString与QByteArray之
将cell类型的数据转换为double类型的函数
double string 转换 /** * This class is converts a Double to a double-digit String * (and vise-versa) by BeanUtils when copying properties. * * @author <a href="mailto:matt@raibledesigns.com">Matt ...
在判断数是否为double时,会用到小数点处理的问题,一个小数点还是多个小数点
主要介绍了java对double数组排序示例,代码简单,下面我们直接上代码,需要的朋友可以参考下
主要介绍了Java中Double保留后小数位的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧