同步一个方法为什么不可取?

huangapple go评论83阅读模式
英文:

Why is synchronizing a method not advisable?

问题

The above code snippet is preferred over declaring getInstance() as synchronized because it employs the double-check locking idiom to achieve lazy initialization while minimizing synchronization overhead. When a method is declared as synchronized, it means that only one thread can execute that method on an instance of the class at a time, ensuring thread safety. However, synchronizing the entire method can slow down performance because it prevents concurrent access even when not necessary. In the provided code, synchronization is applied only when instance is null, reducing the performance impact.

英文:

Consider the below code snippet:

public class DbSingleton {

	private static volatile DbSingleton instance = null;

	private DbSingleton() {
		if (instance != null) {
   			throw new RuntimeException("Use getInstance()");
		}
	}

	public static DbSingleton getInstance() {
		if (instance == null) {
			synchronized (DbSingleton.class) {
				if (instance == null) {
					instance = new DbSingleton();
				}
			}
   		}
   		return instance;
   	}
}

Why is the above preferred to declaring getInstance() as synchronized? What does it mean when a method is declared as synchronized? Will it synchronize the entire class and slow down the performance?

答案1

得分: 1

如果你将getInstance声明为同步的,那么每次调用getInstance时,即使在初始化之后,你都会为同步而付出性能代价。使用这种实现方式,getInstance在第一次调用后将不会进入同步块,因为实例已经被初始化。

英文:

If you declare getInstance as synchronized, you will pay for the synchronization penalty every time you call getInstance even after it is initialized. With this implementation, getInstance will never go into a synchronized block after its first call because the instance is initialized already.

答案2

得分: 1

获得锁是昂贵的。在给定的代码中,只有当 instance == null 时才会获取锁。

考虑以下情况:

  1. 线程 A 调用 getInstance()
  2. 线程 B 调用 getInstance()
  3. 线程 A 获取锁
  4. 线程 B 在锁上等待
  5. 线程 A 检查 instance == null,初始化它,并释放锁
  6. 线程 B 获取锁
  7. 线程 B 检查 instance != null 并返回实例。

这种情况无论是使用同步方法还是双重检查锁定都会发生。然而,在实例化之后,考虑一下差异。

(这与 public static synchronized DbSingleton getInstance() 相同)

public static DbSingleton getInstance() {
    synchronized (DbSingleton.class) {
        if (instance == null) {
            instance = new DbSingleton();
        }
        return instance;
    }
}

使用这种方法,实例初始化后的情况如下:

  1. 线程 A 调用 getInstance,获取锁,返回实例,并释放锁
  2. 线程 B 调用 getInstance,获取锁,返回实例,并释放锁
  3. 等等。

然而,在这种情况下,获取锁是多余的,这正是这段代码的目的:

public static DbSingleton getInstance() {
    if (instance == null) {
        synchronized (DbSingleton.class) {
            if (instance == null) {
                instance = new DbSingleton();
            }
        }
    }
    return instance;
}

如果实例不等于 null,则永远不会获取锁,流程如下:

  1. 线程 A 调用 getInstance 并返回实例 (无锁)
  2. 线程 B 调用 getInstance 并返回实例 (无锁)
英文:

Acquiring a lock is expensive. In the given code, a lock is only acquired if instance == null.

Consider the following

  1. Thread A calls getInstance()
  2. Thread B calls getInstance()
  3. Thread A acquires the lock
  4. Thread B waits on the lock
  5. Thread A checks that instance == null initializes it, and releases the lock
  6. Thread B acquires the lock
  7. Thread B checks that instance != null and returns instance.

This scenario will happen with either a synchronized method or double checked locking. However, after the instance has been instantiated, consider the difference.

(This is the same as public static synchronized DbSingleton getInstance())

public static DbSingleton getInstance() {
    synchronized (DbSingleton.class) {
        if (instance == null) {
            instance = new DbSingleton();
        }
        return instance;
    }
}

With this method, after instance has been initialized..

  1. Thread A calls getInstance, acquires the lock, returns instance, and releases the lock
  2. Thread B calls getInstance, acquires the lock, returns instance, and releases the lock
  3. etc.

However, acquiring the lock is redundant in this case, which is the purpose of this code:

public static DbSingleton getInstance() {
    if (instance == null) {
        synchronized (DbSingleton.class) {
            if (instance == null) {
                instance = new DbSingleton();
            }
        }
    }
    return instance;
}

If instance does not equal null, a lock is never acquired and the flow looks like

  1. Thread A calls getInstance and returns instance (no lock)
  2. Thread B calls getInstance and returns instance (no lock)

答案3

得分: 0

双重检查锁定是不必要的,也是不好的,就像单例反模式一样。此外,将静态变量初始化为null是一个不好的想法。只需将变量初始化为final变量,或者使用枚举。

private static final DbSingleton instance = new DbSingleton();
...
public static DbSingleton getInstance() {
    return instance;
}

当类被初始化时,实例已经被懒惰地初始化,因此任何额外的努力来实现这一点都是愚蠢的。

英文:

Double-checked locking is evil and unnecessary, as is the Singleton antipattern. Also, initializing the static variable to null is a bad idea. Just initialize the variable as a final variable, or use an enum.

private static final DbSingleton instance = new DbSingleton();
…
public static DbSingleton getInstance() {
    return instance;
}

The instance is already lazily initialized when the class is, so any additional effort to accomplish that is stupid.

huangapple
  • 本文由 发表于 2020年7月23日 00:05:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/63038504.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定