双重加锁检查和单例
1. 近日,在做项目中实现集合的异步回调功能,使用了双重加锁检查的机制解决同步问题,原先代码如下:
public void obtainNewestInterface(Context context, UpdateBean config, ObtainListener obtainListener) {
if (mUpdateObject == null) {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
这个函数用于获取一个功能的最新对象,如果此对象已经加载完毕(行3),直接回调(行15)。
否则,将回调加入集合中(行5),如果队列没有在运行加载任务(行9),就可以加载启动任务,加载任务的部分代码如下:
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
mObtainListeners.clear();
}
}
}
加载最新功能对象完成后,将遍历需要加载的集合(行9),一一回调(行11),然后清空(行13)。
这个是没有逻辑问题,但是在多线性同时调用obtainNewestInterface方法的时候,会产生同步的问题,由于没有加入同步锁,会导致集合的对象出现以下错误:
1. 线程A运行至加载完成,遍历回调的时候,线程B运行至mObtainListeners.add(obtainListener);,会出现同步错误。
2. 线程A运行至加载完成,遍历完成的时候,线程B运行至判断是否需要提交任务,这时候size=2,线程A遍历完成,线程B不再提交任务,那么B任务就不会回调
于是加入了锁安全,代码如下:
public void obtainNewestInterface(Context context, UpdateBean config,
ObtainListener obtainListener) {
if (mUpdateObject == null) {
synchronized (mObtainListeners) {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
synchronized (mObtainListeners) {
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
}
mObtainListeners.clear();
}
}
}
这样就解决了如上问题,可是仔细品味一番,新的问题又来了:
1. 线程A执行在遍历,线程B被锁至 if (mUpdateObject == null) {
2.线程B执行遍历完毕,清空集合,此时object不为空,锁解除,线程B进入锁代码,会继续add,此时size=1,会继续提交任务
本质是为了判断object为空就加载一次,回调全部,所以就又加上在锁里面判空,代码如下:
public void obtainNewestInterface(Context context, UpdateBean config,
ObtainListener obtainListener) {
if (mUpdateObject == null) {
synchronized (mObtainListeners) {
//代码加入判空
if (mUpdateObject != null) {
obtainListener.onSucceed(mUpdateObject);
} else {
mObtainListeners.add(obtainListener);
if (mObtainListeners.size() <= 1) {
this.execute(null, context, config);
}
}
}
} else {
obtainListener.onSucceed(mUpdateObject);
}
}
//以下代码不变
@Override
public void onPostExecute(Object result, String taskid) {
if (result != null) {
mUpdateObject = (IAd) result;
synchronized (mObtainListeners) {
for (ObtainListener listener : mObtainListeners) {
listener.onSucceed(mUpdateObject);
}
mObtainListeners.clear();
}
}
}
搞定!
分享到:
相关推荐
"Java双重检查加锁单例模式的详解" Java双重检查加锁单例模式是一种常用的单例模式实现方法,但是在多线程环境下,它存在一些问题。在这篇文章中,我们将探讨Java双重检查加锁单例模式的详解,包括它的优点和缺点,...
在多线程环境下,特别是在Web应用程序中,为了保证在多个并发请求时,不会创建多个实例,我们需要引入加锁机制,即互斥(Mutex),来确保线程安全。 互斥是一种同步机制,用于控制对共享资源的访问,使得在任何时刻...
双重检查加锁模式需要考虑同步机制和`volatile`变量的使用。 **2.3 饿汉模式与静态内部类模式** - **线程安全性**: 静态内部类模式和饿汉模式都线程安全。 - **延迟加载**: 静态内部类模式实现延迟加载;饿汉模式...
JAVA多线程并发下的单例模式应用 单例模式是设计模式中比较简单的一个,也是...在多线程并发下的单例模式应用中,我们需要考虑线程安全问题,可以使用synchronized关键字、双重检查加锁机制或枚举类型来实现单例模式。
}}在双重检查锁(Double-Checked Locking)实现中,第一次检查是在不加锁的情况下进行的,只有当第一次检查后 instance 仍然为 null 时,才会进入同步代码块进行第二次检查和实例化。这种方式提高了并发性能,因为...
4. 加锁机制和双重检查锁机制的应用 5. volatile关键字的作用和应用 在实际项目中,单例模式可以应用于以下场景: 1. 配置文件的读取 2. 数据库连接池的管理 3. 日志记录系统的实现 4. 缓存系统的设计 需要注意的...
2. **双重检查机制**: - 即使使用细粒度锁,也可能会出现多个线程同时进入锁的内部代码段的情况。为了解决这个问题,可以在锁定区域内再次检查数据是否存在,如果存在则直接返回,避免不必要的数据库查询。 3. **...
通过理解并应用双重检查锁定、volatile、延迟初始化以及其他的并发控制策略,开发者可以构建出更加健壮的并发应用程序。在实际项目中,根据需求和性能考虑,可以选择合适的DCL实现方式,以保证代码的稳定性和性能。
- 双重检查加锁机制,第一次检查是为了避免不必要的锁竞争,第二次检查是在获取锁后确认是否已经创建了实例,避免了多个线程同时创建实例的问题。 **方法二:使用装饰器** 第二种方法是使用装饰器来实现单例。首先...
如果未找到,则加锁再次检查,避免了多个线程同时加载同一数据的问题(双重检查锁定模式)。 5. **加载数据源**:`protected static synchronized void loadDataSource(Object key)` 是一个受保护的同步方法,用于...
- 实现加锁并进行双重检查机制,确保只有单一请求能够访问数据库。 - 设置过期时间时加入一定的随机性,避免数据同时失效。 - 对于热点数据考虑设置永不过期或定期续命的方式。 ##### 2. 缓存穿透 - **原因**:...
2. 双重检查锁定(Double-Checked Locking):这是最常见的一种线程安全实现方式。在获取实例时先检查实例是否已经存在,若不存在再加锁并创建。这样可以减少不必要的同步开销。 3. 原子操作:利用C++11引入的std::...
这种方法称为双重检查锁定(Double-Check Locking),既保证了线程安全,又尽量减少了同步的开销。 总的来说,理解和掌握Java中的线程同步和线程协作,以及事务的隔离性,对于开发高效、可靠的并发应用程序至关重要...
这个实现使用了双重检查锁定机制,减少了锁的使用,提高了性能。 总结: C#单例模式是用于确保一个类只有一个实例,并提供一个全局访问点的设计模式。我们可以使用经典的单例模式实现,但是需要解决线程安全问题。...
1. **DCL双锁检测**:在懒汉式单例中,为了避免每次获取实例都进行同步而导致性能下降,可以通过双重检查锁定来优化。但需要注意JVM内存模型中的重排序问题,可能导致对象尚未完全初始化就返回。 2. **多类加载器...
3. **双重检查锁定(DCL,Double Check Locking)单例模式**:结合了饿汉模式和懒汉模式的优点,既延迟了初始化,又保证了线程安全。在多线程环境下,只有在`instance`为`null`时才会进入同步块,避免了不必要的同步...
这种方法首先在不加锁的情况下检查`instance`是否为`null`,如果为`null`则进行同步操作: ```java public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if ...
事务执行时不加锁,只有在提交时才检查是否有冲突发生。如果发现冲突,则回滚事务重新执行。这种方法可以提高系统的并发度,但可能增加事务的重试次数。 3. **动态调整串行化顺序的乐观并发控制**:这是一种优化后...
- 通常可以通过加锁机制来确保多个进程同时写入同一个文件时的原子性和一致性。在PHP中,可以使用`flock`函数来实现文件锁。 ### 8. PHP实现一个栈 - 栈是一种后进先出(LIFO)的数据结构。在PHP中,可以利用数组...
Java中线程安全的单例通常使用枚举或双重检查锁定(Double-Checked Locking)实现。 3. **观察者设计模式**:观察者模式是一种行为设计模式,当一个对象的状态改变时,所有依赖于它的对象都会得到通知并自动更新。...