Single example mode completely analyzed (2) ---- Exploring simple but make people confused single example mode

zhaozj2021-02-16  52

Test Sample Mode Next, I use JUnit corresponding to log4j to test a single case, which will run through the remaining parts of this article. If you are not familiar with JUnit or Log4j, please refer to the relevant resources.

Example 2 is a case in a single example pattern of JUnit Test Example 1:

Example 2. Case of a single sample mode

import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public class SingletonTest extends TestCase {private ClassicSingleton sone = null, stwo = null; private static Logger logger = Logger.getRootLogger (); Public Singletontest (String Name) {Super (Name);} public void setup () {logger.info ("getting singleleton ..."); SONE = classicsingleton.getinstance (); logger.info ("... got singleleton : " SONE); Logger.info (" getting singleton ... "); STWO = Classicsingleton.getInstance (); logger.info (" ... Got Singleton: " STWO);} public void testunique ()} Logger.info ("Checking Singletons for Equality); Assert.Assertequals (True, Sone == STWO);}}

Example 2 calls classicsingleton.getInstance () twice, and stores the returned reference in the member variable. Method testUnique () will check these references to see if they are the same. Example 3 is the output of this test case:

Example 3. Yes, the output of this test case

BuildFile: build.xmlinit: [Echo] Build 20030414 (14-04-2003 03:08) Compile: Run-test-text: [java] .info main: [b] getting singleton ... [/ b] [java ] Info main: [b] created singleton: [/ b] Singleton @ E86f41 [java] info main: ... got Singleton: Singleton @ E86f41 [java] info main: [b] getting singleleton ... [/ b] [java] info main: ... Got Singleton: Singleton @ e86f41 [java] info main: checking singletons for equality [java] Time: 0.032 [java] OK (1 test)

As shown in the list, the simple test of Example 2 passed successfully ---- Two single-class classes obtained by classicsingleton.getInstance () did the same; however, you have to know that these references are available in single-threading. . The following part focuses on multithreaded test singles.

Considering multithreaded factors

In Example 1 Classicsingleton.GetInstance () method Due to the following code instead of thread secure: 1: if (instance == null) {2: instance = new singleleton (); 3:}

If a thread is switched before the assignment statement of the second row, the member variable instance is still null, and then the other thread may be next to the IF block. In this case, two different single case instances are created. Unfortunately, this hypothesis has rarely happened so that this hypothesis is also difficult to appear during the test (translation: In this might be authors, the author is unable to test, causing people to make people relaxation and feel sighed). In order to demonstrate this thread, I have to realize the class in Example 1. Example 4 is a revised single example:

Example 4. Human arrangement

import org.apache.log4j.Logger; public class Singleton {private static Singleton singleton = null; private static Logger logger = Logger.getRootLogger (); private static boolean firstThread = true; protected Singleton () {// Exists only to defeat instantiation .} public static Singleton getInstance () {if (singleton == null) {simulateRandomActivity (); singleton = new Singleton ();} logger.info ( "created singleton:" singleton); return singleton;} private static void simulateRandomActivity () {Try {if (firstthread = false; logger.info ("Sleeping ..."); // this nap shop give the second thread enough time // to get by the first thread. thread.currentthread ( ) .sleep (50);}} catch (interruptedException ex) {logger.warn ("Sleep Interrupted");}}}

In addition to using a multi-threaded error handling in this list, an example 4 is similar to a single case class in Example 1. When the GetInstance () method is called for the first time, the thread calling this method will sleep for 50 milliseconds to other threads also have time to call GetInstance () and create a new single-size instance. When you sleep thread awakening, it creates a new single-size instance so that we have two single-size instances. Although Example 4 is human so, it simulates the first thread to call GetInstance () and the true situation that is switched when not completed.

Example 5 Tested the singular class class:

Example 5. Failure test

import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public class SingletonTest extends TestCase {private static Logger logger = Logger.getRootLogger (); private static Singleton singleton = null; public SingletonTest ( String name) {super (name);} public void setUp () {singleton = null;}. public void testUnique () throws InterruptedException {// Both threads call Singleton.getInstance () Thread threadOne = new Thread (new SingletonTestRunnable () ), threadTwo = new Thread (new SingletonTestRunnable ()); threadOne.start (); threadTwo.start (); threadOne.join (); threadTwo.join ();} private static class SingletonTestRunnable implements Runnable {public void run () {// Get a reason to the Singleton. Singleton S = Singleton.GetInstance (); // Protect Singleton Member Variable from // Multithreaded Access. Synchronized (Singletontest.class) { IF (Singleton == NULL) // if Local Reference Is Null ... Singleton = S; // ... set it to the singleleton} // local reference must be equal to the one and // only instance of singleton; OtherWise, We Have TWO // Singleton Instances. Assert.assertequals (TRUE, S == Singleton);}}}}}} The test case of Example 5 creates two threads, and then starts, waiting to be completed. This case maintains a static reference to a single case, and each thread calls Singleton.getInstance (). If this static member variable is not set, the first thread will set it to the reference to getInstance (), and then this static variable is compared to a part variable equal.

A series of things happens when this test case is run: the first thread calls GetInstance (), enters the IF block, and then sleeps; then, the second thread also calls GetInstance () and creates an instance of a single class. The second thread sets this static member variable to the reference it created. The second thread checks this static member variable equal to a partial backup. Then the test passed. When the first thread is awake, it creates an instance of a single case, and it does not set the static member variable (because the second thread has been set), the static variable is detached from the local variable. , The phase of the test failure. Example 6 lists the output of Example 5: Example 6. Example 5 output

Buildfile: build.xmlinit: [Echo] Build 20030414 (14-04-2003 03:06) Compile: Run-test-text: info thread-1: sleeping ... Info Thread-2: Created Singleton: Singleton @ 7e5cbdinfo thread -1: created singleton: Singleton@704ebbjunit.framework.AssertionFailedError: expected: but was: at junit.framework.Assert.fail (Assert.java:47) at junit.framework.Assert.failNotEquals (Assert.java:282) at junit.framework.Assert.assertEquals (Assert.java:64) at junit.framework.Assert.assertEquals (Assert.java:149) at junit.framework.Assert.assertEquals (Assert.java:155) at SingletonTest $ SingletonTestRunnable.run (Unknown Source) at java.lang.thread.run (thread.java: 554) [java]. [Java] Time: 0.577 [java] OK (1 test)

So far we now know that Example 4 is not a thread, let us see how to fix it.

Synchronize

It is easy to make the single example of Example 4 for threads.

Public synchronized static singleton getinstance () {if (Singleton == Null) {simulaterAndomactivity (); singleton = new singleleton ();} logger.info ("Created Singleton;}; return

After synchronizing the GetInstance () method, we can get the following results of the test case returned by Example 5:

BuildFile: Build.xmlinit: [Echo] Build 20030414 (14-04-2003 03:15) Compile: [Javac] Compiling 2 Source Filesrun-Test-Text: Info Thread-1: Sleeping ... Info Thread-1: Created Singleton: Singleton @ EF577DINFO Thread-2: Created Singleton: Singleton @ EF577D [Java]. [Java] Time: 0.513 [Java] OK (1 Test) This, this test case works normally, and multi-thread troubles are also resolved However, alert reader may recognize that the getInstance () method only needs to synchronize when called first call. Because the synchronization performance overhead is expensive (synchronous method can be reduced to 100 times than the non-synchronous method), maybe we can introduce a performance improvement method, which is only synchronized with the assignment statement in the GetInstance () method of the single case.

Performance improvement method

When looking for a performance improvement method, you may choose to override the getInstance () method like this:

Public static singleton getinstance () {if (Singleton == null) {synchronized (singleton.class) {Singleton = new singleleton ();}} return singleton;}

This code snippet only synchronizes the critical code, not synchronous. However, this code is not a thread. Consider the following assumptions: Thread 1 enters the synchronization block, and thread 1 is switched before it assigns the Singleton member variable. The other thread will then enter the IF block. The second thread will wait until the first thread is complete and still get two different single-case instances. Is there a way to fix this problem? Please read it.

Double lock check

It seems that the double lock inspection seems to be a technique that instantiates the lazy Chinese to thread safety. The following code snippet shows this technology:

Public static singleleton getInstance () {if (Singleton == null) {singleton == null) {singleton = new singleleton ();}}}} return singleton;

What happens if the two threads accesses the getInstance () method? Imagine the thread 1 to perform synchronization blocks, it is handover. Next, the second thread enters the IF block. When thread 1 exits the synchronization block, thread 2 will re-check if the Singleton instance is still NULL. Because the thread 1 sets the Singleton member variable, the second check of thread 2 will fail, and the second single case instance will not be created. It seems to be the case.

Unfortunately, dual lock checks will not guarantee normal work because the compiler will assign a value to Singleton before the Singlet constructor is called. If the thread 1 is switched before the Singlet is started after the Singleton is assigned, the thread 2 will be returned to a reference to the uninitialized single-class instance.

Some example mode for improved thread safety

Example 7 lists a simple, fast and thread-safe single-case model implementation:

Example 7. A simple single case

Public class singleton {public final static singleleton instance = new singleleton (); private singleton () {// exissrs} This code is that the thread is safe because static member variables will be in the first time. Created when access. You got an implementation of a thread security that automatically uses a lazy-style instantiated; you should use it like this:

Singleton Singleton = Singleton.Instance; Singleton.doth (); Singleton.dothat (); ...

Of course, all things are not perfect, the previous Singleton is just a compromise; if you use that implementation, you can't change it so that you may want to allow multiple single-size instances. Implement with a more crushed single-case mode (by a getInstance () method) You can change this method to return a unique instance or one of hundreds of instances. You can't do this with an open and static member variable.

You can safely use the single sample mode of Example 7 or the implementation of the GetInstance () method with a synchronized GetInstance () method. However, we must study another question: You must specify this single case class in the compile period, so it is not very flexible. A single-case registry will allow us to specify a single case class in the run period.

Sustainable continued.

转载请注明原文地址:https://www.9cbs.com/read-22562.html

New Post(0)