Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the problem of DCL and Singleton mode

2025-01-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains "how to solve the problem of DCL and Singleton mode". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let Xiaobian take you to learn "how to solve the problem of DCL and Singleton mode"!

Singleton pattern encounters a number of problems in multithreaded situations, a simple example

1: class Singleton { 2: private static Singleton instance = null; 3: 4: public static Singleton instance() { 5: if (instance == null) { 6: instance = new Singleton(); 7: } 8: return instance; 9: } 10: }

Suppose such a scenario, there are two threads calling Singleton.instance(), first thread 1 judges whether instance is equal to null, and after judging, the virtual machine schedules thread 2 as a running thread. Thread 2 judges whether instance is null again, and then creates a Singleton instance. After thread 2 runs out of time slice, thread 1 is awakened, and the code it executes next is still instance = new Singleton();

Two calls returned different objects, and there was a problem.

The simplest way is naturally to initialize the object as soon as the class is loaded: private static Singleton instance = new Singleton();

The Java Language Specification (JLS) states that a class can only be initialized once, so this is certainly no problem.

But if you want to implement Lazy initialization, for example, the parameters of this instance initialization can only be determined at runtime, what should you do?

There is still the easiest way: use the synchronized keyword to modify the initialization method:

public synchronized static Singleton instance() { if (instance == null) { instance = new Singleton(); } return instance; }

There is a performance issue here: when multiple threads access this method simultaneously, synchronization causes only one thread to run at a time, affecting program performance. In fact, after initialization, you simply need to return a reference to instance.

Dual detection lock solution

DCL is a "seemingly" effective solution, so put the corresponding code up first:

1 : class Singleton { 2 : private static Singleton instance = null ; 3 : 4 : public static Singleton instance() { 5 : if (instance == null ) { 6 : synchronized (this) { 7 : if (instance == null) 8 : instance = new Singleton(); 9 : } 10 : } 11 : return instance; 12 : } 13 : }

This is smart, but broken. Look at the reasons:

Java compilers schedule instructions to improve program performance, CPU executes instructions out of order for performance reasons (at least most general-purpose processors now in use are out-of-order), and the existence of cache also changes the order in which data is written back into memory.[2] JMM(Java Memory Model, see [1]) states that all of these optimizations are permissible, as long as the results are the same as if executed strictly in sequence.

Java assumes that each thread runs on its own processor, enjoys its own memory, and interacts with shared main memory. Note that this model makes sense even on a single core, considering that caches and registers hold some temporary variables. In theory, each thread must update its main memory content immediately after modifying its own memory. But Java designers thought this constraint would affect program performance, and they tried to create a memory model that would make programs run faster but still keep threads interacting as expected.

The synchronized keyword is one of these tools. In fact, the implementation of synchronized blocks is different from semaphore in Linux, in which lock acquisition and release both raise a Memory Barrier to force synchronization between thread local memory and main memory. Through this mechanism, synchronization mechanisms in Java guarantee the atomicity of instructions in synchronized blocks.

Double-detect locking problem

Okay, back to the DCL problem. It seems that accessing an unsynchronized instance field will not cause any problems. Let's assume a scenario again:

Thread 1 enters the synchronization block and executes instance = new Singleton(); Thread 2 just starts executing getResource();

In order, the next steps to be performed are 1) Allocate memory for the new Singleton object 2) Call Singleton's constructor to initialize the member fields 3) Instance is assigned as a reference to the new object.

As mentioned above, it is possible for a compiler or processor to execute instructions out of order in order to improve performance. The real execution steps of thread one may be 1) allocate memory 2) instance points to a new object 3) initialize a new instance. If thread 2 wakes up after 2 and before 3 executes, it sees a non-null instance, jumps out of the method body, and leaves with an uninitialized Singleton object.

This is one case where errors occur, and for a more detailed description of problems caused by compiler instruction scheduling, see this page [4].

[3]Evidence of a compiler instruction schedule is provided in

instance = new Singleton(); this command is compiled in Symantec JIT

0206106A mov eax,0F97E78h 0206106F call 01F6B210 ; Allocation space 02061074 mov dword ptr [ebp],eax ; EBP holds the address of instance 02061077 mov ecx,dword ptr [eax] ; dereference, get new pointer address 02061079 mov dword ptr [ecx],100h ; The next four lines are inline constructor 0206107F mov dword ptr [ecx+4],200h 02061086 mov dword ptr [ecx+8],400h 0206108D mov dword ptr [ecx+0Ch],0F84030h

As you can see, assignment is done before initialization, which JLS allows.

Another scenario is to assume that as soon as the thread finishes initializing the Singleton object safely, it exits the synchronization block and synchronizes with local memory and main memory. Thread 2 sees a non-empty reference and takes it. Note that thread 2 does not execute a Read Barrier because it does not enter the following synchronization block at all. So it's possible that the data it's seeing is stale.

There are many others who propose fix after fix based on several known methods, but eventually more problems arise. See also [3].

[5]Also explains why errors cannot be avoided even if the instance field is declared volatile.

As you can see, there are generally only two ways to construct a safe Singleton: create the instance when the class loads, or use the poorly performing synchronized method.

At this point, I believe everyone has a deeper understanding of "how to solve the problem of DCL and Singleton mode." Let's do it in practice. Here is the website, more related content can enter the relevant channels for inquiry, pay attention to us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report