The question raised by the double lock implementation singleton

The following is the concrete code for the double lock implementation singleton

public Singleton{

	private static volatile Singleton singleton;

	private Singleton(a){};

	public static Singleton  getSingleton(a){
		if(singleton==null) {synchronized(Singleton.class){
				if(singleton==null){
					singleton=newSingleton(); }}}returnsingleton; }}Copy the code

Synchronized locks the Singleton class. Typically we create locks like this:

Object object=new Object();
synchronized(object){
	/ /...
	/ / critical region
	/ /...
}
Copy the code

So why did the classical implementation of the above singleton choose to use class locking? What is the difference between a lock class and a lock object? What exactly does a class lock lock?

Before we answer the question let’s reacquaint ourselves with some basic uses of synchronized.

class Demo{
  // Decorates non-static methods
  synchronized void foo(a) {
    / / critical region
  }
  // Decorate static methods
  synchronized static void bar(a) {
    / / critical region
  }
  // Decorates the code block
  Object obj = newThe Object ();void baz(a) {
    synchronized(obj) {
      / / critical region}}}Copy the code

In the above three uses, we can see that when we modify a code block, we lock the object obj, but when we modify static and non-static methods, what exactly are we locking? There is an implicit rule in Java:

When you decorate a static method, you lock the Class object of the current Class, which in the example above is demo.class. When you modify a non-static method, you lock the this object, the current instantiation object.Copy the code

What is a Class Class?

The relationship between Java ordinary classes and Class classes can be understood as follows:

// Everything is an objectEverything = objects// Classes are abstractions of objectsClass <------(extract the image part)------- object//Class is an abstraction of a ClassClass <------(extract the image part)------- Class/ / in conclusionClass Class < -- -- -- -- -- - (take out as part of the Class -- -- -- -- -- -- -- < -- -- -- -- -- - (take out as part of the) -- -- -- -- -- -- -- object = all thingsCopy the code

In a sense, there are two kinds of objects in Java: instance objects and Class objects. The runtime type information for each Class is represented through the Class Class. Java uses Class objects to perform its RTTI (run-time Type Identification), which is how polymorphism is implemented.

Each Class has a Class object, and a corresponding Class object is generated each time a new Class is compiled. Basic types such as int long float double char byte short Boolean have corresponding Class objects, as well as arrays and void (void. Class).

Class classes have no public constructor, so there is no way to explicitly declare a Class object, which is automatically constructed by the Java virtual machine by calling the defineClass method in the Class loader.

What is the difference between object locking and class locking?

As can be seen from the above, object lock and class lock are essentially a kind of lock, the difference is that a class can have multiple object locks, but only one class lock. Therefore, using synchronized to lock a class is unique. When one thread acquires the class lock, other threads block any method that requires the class lock. In general, locklike devices are unique

Let me rewrite an example using object locks:

public Singleton{

	private static volatile Singleton singleton;

	private Singleton(a){};

	public static Singleton getSingleton(a){
		Object obj=new Object();
		if(singleton==null) {synchronized(obj){
				if(singleton==null){
					singleton=newSingleton(); }}}returnsingleton; }}Copy the code

This may seem like no problem, but in the case of multiple threads creating multiple Singleton objects, let’s do a simple test

public class Singleton {
    private static volatile Singleton singleton;

    private Singleton(a){};

    public static Singleton getSingleton(a){
        Object obj=new Object();
        if(singleton==null) {synchronized(obj){
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(singleton==null){
                    System.out.println("Create a singleton object");
                    singleton=newSingleton(); }}}return singleton;
    }

    public static void main(String[] args) {
        Thread thread1=new Thread(Singleton::getSingleton);
        Thread thread2=new Thread(Singleton::getSingleton);
        Thread thread3=new Thread(Singleton::getSingleton);
        Thread thread4=newThread(Singleton::getSingleton); thread1.start(); thread2.start(); thread3.start(); thread4.start(); }}Copy the code

The results

Creating a singleton Creating a singleton creating a singleton creating a singleton creating a singleton Process Finished with exit code0
Copy the code

Here we see new four times. Why is that? Synchronized (obj) instantiates a new Object as an Object lock each time it is executed. These locks are not related to each other, so locking and unlocking are virtually indistinguishable, and mutual exclusion does not occur. This is where the use of class locks comes in.

conclusion

Kind of locks:

Each Class has a unique Class object, the creation of which is managed by the JVM. Locking a Class object is globally unique, which we call Class locking.Copy the code

Object lock:

Class instantiation object can have more than one, each instantiation object locking is non-interference, they do not have global uniqueness, we call it object lock. Object locks can generally be used to create fine-grained locks.Copy the code


b y \color{#34a853}{b}\color{#ea4335}{y}

W z b \color{#4285f4}{W}\color{#ea4335}{z}\color{#fbbc05}{b}