java 双重检查锁定的使用 (Double-checked locking)单列模式

代码来自与 SonarQube
其它外部的文档 The “Double-Checked Locking is Broken” Declaration

描述

Double-checked locking is the practice of checking a lazy-initialized object’s state both before and after a synchronized block is entered to determine whether or not to initialize the object.

It does not work reliably in a platform-independent manner without additional synchronization for mutable instances of anything other than float or int. Using double-checked locking for the lazy initialization of any other type of primitive or mutable object risks a second thread using an uninitialized or partially initialized member while the first thread is still creating it, and crashing the program.

There are multiple ways to fix this. The simplest one is to simply not use double checked locking at all, and synchronize the whole method instead. With early versions of the JVM, synchronizing the whole method was generally advised against for performance reasons. But synchronized performance has improved a lot in newer JVMs, so this is now a preferred solution. If you prefer to avoid using synchronized altogether, you can use an inner static class to hold the reference instead. Inner static classes are guaranteed to load lazily.

翻译:

双重检查锁定是在进入同步块之前和之后检查延迟初始化对象的状态以确定是否初始化对象的一种实践。

如果不对float或int以外的任何可变实例进行额外同步,则无法以独立于平台的方式可靠地工作。如果对任何其他类型的原始对象或可变对象进行延迟初始化使用双重检查锁定,则在第一个线程仍在创建第二个线程时,该线程将使用未初始化或部分初始化的成员,这会导致程序崩溃。

有多种解决方法。最简单的方法是根本不使用双重检查锁定,而是同步整个方法。对于早期版本的JVM,出于性能原因,通常建议不要同步整个方法。但是,在新的JVM中,同步性能已大大提高,因此,现在这是首选的解决方案。如果您希望完全避免使用同步,则可以使用内部静态类来保存引用。内部静态类保证延迟加载。

不规范的代码示例

@NotThreadSafe
public class DoubleCheckedLocking {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null) {
            synchronized (DoubleCheckedLocking.class) {
                if (resource == null)
                    resource = new Resource();
            }
        }
        return resource;
    }

    static class Resource {

    }
}

规范的代码

1.整个方法同步

@ThreadSafe
public class SafeLazyInitialization {
    private static Resource resource;

    public synchronized static Resource getInstance() {
        if (resource == null)
            resource = new Resource();
        return resource;
    }

    static class Resource {
    }
}

2.延迟初始化

@ThreadSafe
public class ResourceFactory {
    private static class ResourceHolder {
    	// This will be lazily initialised
        public static Resource resource = new Resource(); 
    }

    public static Resource getResource() {
        return ResourceFactory.ResourceHolder.resource;
    }

    static class Resource {
    }
}

3.使用 volatile 关键字

class ResourceFactory {
  private volatile Resource resource;

  public Resource getResource() {
    Resource localResource = resource;
    if (localResource == null) {
      synchronized (this) {
        localResource = resource;
        if (localResource == null) {
          resource = localResource = new Resource();
        }
      }
    }
    return localResource;
  }

  static class Resource {
  }
}

jdk 13 synchronized 的优化

synchronized 在较新的JVM中,性能已大大提高,因此现在是首选解决方案。

下面摘自 java 13 版本中的 CopyOnWriteArrayList 的代码。

    public boolean add(E e) {
        synchronized (lock) {
            Object[] es = getArray();
            int len = es.length;
            es = Arrays.copyOf(es, len + 1);
            es[len] = e;
            setArray(es);
            return true;
        }
    }

	public E set(int index, E element) {
        synchronized (lock) {
            Object[] es = getArray();
            E oldValue = elementAt(es, index);

            if (oldValue != element) {
                es = es.clone();
                es[index] = element;
            }
            // Ensure volatile write semantics even when oldvalue == element
            setArray(es);
            return oldValue;
        }
    }

    public E remove(int index) {
        synchronized (lock) {
            Object[] es = getArray();
            int len = es.length;
            E oldValue = elementAt(es, index);
            int numMoved = len - index - 1;
            Object[] newElements;
            if (numMoved == 0)
                newElements = Arrays.copyOf(es, len - 1);
            else {
                newElements = new Object[len - 1];
                System.arraycopy(es, 0, newElements, 0, index);
                System.arraycopy(es, index + 1, newElements, index,
                                 numMoved);
            }
            setArray(newElements);
            return oldValue;
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值