- 浏览: 33222 次
- 性别:
- 来自: 北京
-
最新评论
-
Corwen:
INSERT ... ON DUPLICATE KEY UPD ...
mysql insert的几点操作(DELAYED 、IGNORE、ON DUPLICATE KEY UPDATE )
对于多线程编程来说,同步问题是我们需要考虑的最多的问题,同步的锁什么时候加,加在哪里都需要考虑,当然在不影响功能的情况下,同步越少越好,锁加的越迟越优是我们都必须认同的。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!
发表评论
-
apache 中的EqualsBuilder和HashCodeBuilder
2012-05-04 10:27 1025自动化hashCode()和equals() 问题产生:当需 ... -
JDK5.0新特性系列---11.4线程 Condition
2012-05-02 11:05 813import java.util.concurrent. ... -
JDK5.0新特性系列---11.6线程 BlockingQueue
2012-05-02 11:05 795importjava.util.concurrent. ... -
JDK5.0新特性系列---11.5.4线程 同步装置之Exchanger
2012-05-02 11:05 748/** * Exchanger让两个线程互换信息 ... -
JDK5.0新特性系列---11.5.2线程 同步装置之CountDownLatch
2012-05-02 11:05 860import java.util.concurrent ... -
JDK5.0新特性系列---11.5.1线程 同步装置之Semaphore
2012-05-03 10:54 767import java.util.ArrayList; ... -
JDK5.0新特性系列---11.2线程 任务执行架构
2012-05-03 10:54 874import java.util.concurrent. ... -
JDK5.0新特性系列---11.3线程 锁Lock
2012-05-03 10:54 796import java.util.concurrent. ... -
JDK5.0新特性系列---11.1线程 Callable和Future
2012-05-03 10:54 1230import java.util.concurrent. ... -
JDK5.0新特性系列---8.泛型编程
2012-04-28 08:58 850import java.util.ArrayList; ... -
JDK5.0新特性系列---10.监控与管理虚拟机
2012-04-28 08:58 906import java.lang.management ... -
JDK5.0新特性系列---9.注释功能Annotation
2012-04-28 08:58 824import java.lang.annotation. ... -
JDK5.0新特性系列---7.使用ProcessBuilder执行本地命令
2012-04-28 08:57 944import java.io.BufferedReade ... -
JDK5.0新特性系列---6.格式化输出
2012-04-28 08:57 628import java.util.Date; / ... -
JDK5.0新特性系列---5.可变长参数Varargs
2012-04-26 21:53 600/** * 在J2SE5.0之前,当传入到方法的参数 ... -
JDK5.0新特性系列---4.静态导入
2012-04-26 21:51 740/** *静态导入:是指可以import类的静态方法 ... -
JDK5.0新特性系列---3.枚举类型
2012-04-26 21:50 643/** *enum关键字表示枚举类型,它的作用相当于 ... -
JDK5.0新特性系列---2.新的for循环
2012-04-26 21:48 689import java.util.ArrayList; ... -
JAVA移位运算符)
2012-04-20 13:32 860移位运算符就是在二 ... -
GlassFish安装下载和Eclipse热部署
2012-04-13 09:23 2115一、GlassFish安装配置之前需要先安装配置好JDK和An ...
相关推荐
- **单例模式**:在多线程环境下,双重检查锁定(Double-Check Locking)是实现线程安全的单例模式。 5. **JVM优化** 面对高并发场景,优化JVM的性能至关重要。这包括调整内存分配(堆、栈、元空间等),设置垃圾...
3. **单例模式**:在多线程环境中,保证只有一个实例存在,通常使用双重检查锁定(Double-Check Locking)或静态内部类等方式实现。 4. **线程池模式**:通过ThreadPoolExecutor管理线程,可以有效控制并发数量,...
- 双重检查锁定(Double-Check Locking)与初始化-on-demand holder类设计模式。 5. **IO流与NIO** - 流的分类:字节流与字符流,输入流与输出流,缓冲流与转换流。 - 文件操作:File类的常用方法,文件复制与...
7. **Double-Check Locking模式** 这是一种常见的单例模式实现,但如果不正确地使用,可能会导致线程安全问题。JMM通过volatile关键字和synchronized的结合,可以保证在多线程环境下的正确初始化。 8. **死锁** ...
- **Double-Check Locking**:理解双重检查锁定模式,以及其在单例模式中的应用。 - ** volatile与synchronized的区别**:深入探讨两者的异同。 通过本课程的学习,开发者不仅能掌握Java并发编程的基础知识,还能...
在多线程环境下,实现线程安全的单例有几种方法:饿汉式(静态常量)、懒汉式(静态代码块)、双检锁/双重校验锁(DCL,Double-Check Locking)以及静态内部类。其中,DCL是最为推荐的方式,因为它结合了延迟初始化...
3. **线程同步**:为避免多线程环境中的数据不一致性,Java提供了多种同步机制,如synchronized关键字、Lock接口(ReentrantLock)、Semaphore信号量、CountDownLatch倒计时锁和CyclicBarrier同步屏障等。...
另一个例子是线程安全的单例模式,通过`synchronized`关键字或`Double-Check Locking`策略确保在多线程环境中正确地创建和访问单个实例。 在多线程编程中,同步是非常关键的概念。Java提供了多种同步机制,如`...
- **双检锁/双重校验锁(DCL,Double-Check Locking)**:用于创建单例模式,保证线程安全。 - **读写锁**:在多读少写的情况下提高性能,如`ReadWriteLock`接口及其实现类`ReentrantReadWriteLock`。 6. **线程...
- **线程安全初始化**:使用Double-Check Locking或Initialization-on-Demand Holder Class模式初始化单例。 - **避免长时间阻塞操作**:尽量减少I/O、网络等可能导致长时间阻塞的操作在主线程中执行。 以上只是...
- 双重检查锁定模式 (Double-Check Locking) 通过分析`hakesashou`这个文件名,可能是一个名为“筷子手”(谐音)的并发工具或框架,具体功能需要查看源码来了解。学习和理解这些Java高并发编程的知识点,将有助于...
9. **Double-Check Locking(双重检查锁定)**:这是一种优化的单例模式实现,确保在多线程环境下只实例化一次对象,同时避免了同步的性能开销。 10. **静态工厂方法**:通过静态工厂方法创建线程安全的类实例,...
例如,在双重检查锁定模式(Double-Check Locking)的单例实现中,`volatile`关键字用于确保实例在创建完成后对所有线程可见,并防止在实例化过程中出现的错误同步。 3. 使用场景: - 标记状态标志:当需要一个...
- **线程安全**:线程不安全问题分析,如Double-Check Locking、ThreadLocal等。 4. **设计模式** - **六大设计原则**:单一职责、开放封闭、里氏替换、依赖倒置、接口隔离、迪米特法则。 - **常用设计模式**:...
在这个主题下,“DCL常用设计方法”通常指的是如何在Java等编程语言中实现Double-Check Locking(双重检查锁定)和其他相关的设计模式来优化并发性能。以下将详细介绍DCL及其相关的知识点。 1. 双重检查锁定...
`@Getter(lazy=true)` 实现了懒加载,通常用于 Double Check Lock 模式,避免不必要的初始化。`@Log` 系列注解则自动创建日志对象,支持多种日志实现,如 Commons Log, JDK Logging, Log4j 等。 总的来说,Lombok ...
13. @Getter(lazy=true):可以替代经典的Double Check Lock样板代码。 14. @Log:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类:@CommonsLog、@Log、@Log4j、@Log4j2、@Slf4j、@...
在上述的双检锁/双重检查锁定(Double-Check Locking)实现中,`volatile`确保了当`singleton`被初始化为`new Singleton()`时,所有线程都能看到这个新创建的对象,而不是看到旧的`null`值。 接下来,我们探讨`...
例如,作为共享状态的标志,用于多线程间的通信,例如单例模式的双重检查锁定(Double-Check Locking)中,volatile可以确保线程安全地初始化单例。 5. **原理**:在底层实现上,volatile变量在汇编代码中会包含一...
要避免这种情况,可以使用双检锁(Double-Check Locking)或其他线程安全的单例实现方式。 2. **同步访问**:如果反射操作涉及修改共享对象的状态,必须确保这些操作在同步块内进行。例如,使用synchronized关键字...