Remember once synchronized lock string thrown by the pit and then talk about the Java string

Source: Internet
Author: User

Problem description

Business has a need, I describe the problem:

Through proxy IP access to a foreign site n, each IP corresponding to a fixed site n Cookie,cookie has a failure time.

Concurrent, take IP is a certain policy, take IP after IP to take the cookie, found that the cookie exceeds the expiration time, then call the script to access the site n get data once.

In order to prevent multi-threaded access to the same IP, while discovering that the IP corresponding to the cookie invalidation, while the call Script update cookie, for IP plus lock. In order to ensure the global uniqueness of the lock, a prefix is added in front of the lock to identify the business, using synchronized (lock) {} to lock the "lock prefix +ip", so that multi-threaded access to the same IP, and only one IP will update the cookie.

Do not know whether the problem is clear, not clear it doesn't matter, write a test code:

 Public classStringthreadImplementsRunnable {Private Static FinalString lock_prefix = "XXX---"; PrivateString IP;  Publicstringthread (String IP) { This. IP =IP; } @Override Public voidrun () {String lock=Buildlock (); synchronized(lock) {System.out.println ("[" + jdkutil.getthreadname () + "] started running"); //Hibernate 5-Second impersonation script callJdkutil.sleep (5000); System.out.println ("[" + jdkutil.getthreadname () + "] End Run"); }    }        PrivateString Buildlock () {StringBuilder sb=NewStringBuilder ();        Sb.append (Lock_prefix);                Sb.append (IP); String Lock=sb.tostring (); System.out.println ("[" + jdkutil.getthreadname () + "] built the lock [" + Lock + "]"); returnBlock; }    }

Simply put, pass in an IP, try to build a globally unique string, and lock on the string. The expected result is concurrent, such as 5 threads into the same IP, they build the lock is the string "XXX---192.168.1.1", then the 5 threads for synchronized block, should be executed serially, that is, one run completed and then run another, but actually not.

Write a test code, open 5 threads to see the effect:

 Public classStringthreadtest {Private Static Final intThread_count = 5; @Test Public voidTeststringthread () {thread[] threads=NewThread[thread_count];  for(inti = 0; i < Thread_count; i++) {Threads[i]=NewThread (NewStringthread ("192.168.1.1")); }                 for(inti = 0; i < Thread_count; i++) {Threads[i].start (); }                 for (;;); }    }

The result of the execution is:

[Thread-1] built the lock [xxx---192.168.1.1][thread-1] began to run [Thread-3] built the lock [xxx---192.168.1.1][thread -3] began to run [Thread-4] built the lock [XXX---192.168.1.1][thread-4] began to run [thread-0] built the lock [ XXX---192.168.1.1][thread-0] started running [Thread-2] built the lock [XXX---192.168.1.1][thread-2] Start running[thread-1] EndRun [thread-3]end Run [thread-4] End run [thread-0] End Run [ Thread-2] ended up running

See Thread-0, Thread-1, Thread-2, Thread-3, Thread-4 5 Threads Although the locks are built in the same "XXX-192.168.1.1", the code is executed in parallel, which does not fit our expectations.

On the one hand, I was really careless about the fact that there was a problem with the synchronization control in the code, and on the one hand it reflected that my understanding of string was not deep enough, so I wrote an article to record the problem and write a clear explanation of the cause of the problem and how to solve it.

Cause of the problem

Since the problem arises, it should be deduced from the results and the cause of the problem should be found. First look at the code for the Synchronized section:

@Override  Public void run () {    = buildlock ();     synchronized (lock) {        System.out.println ("[" + jdkutil.getthreadname () + "] starts running");         // Hibernate 5-Second impersonation script call        Jdkutil.sleep (the);        System.out.println ("[" + jdkutil.getthreadname () + "] End ran");}    }

Since synchronized locks the object, it is guaranteed that code execution in the synchronous code block is serially executed if the locked object is the same , so since multithreading is executed in parallel in the synchronized part, Then it can be inferred that multiple threads are passed into the same IP, and the built-in lock string is not the same.

Next, look at the code that builds the string:

Private String Buildlock () {    new  StringBuilder ();    Sb.append (lock_prefix);    Sb.append (IP);             = sb.tostring ();    System.out.println ("[" + jdkutil.getthreadname () + "] built the lock [" + Lock + "]");             return lock;}

Lock is generated by StringBuilder, look at the ToString method of StringBuilder:

 Public String toString () {    //  Create a copy, don ' t share the array    returnnew
     String (value, 0, Count);}

So here's the reason: Although the string constructed by the Buildlock () method is "XXX-192.168.1.1", the ToString () method of StringBuilder is always a new string. , so the objects that buildlock out are different objects.

How to solve?

The cause of the above problem is found, that is, every time StringBuilder built objects are new out of the object, then how to solve? Here I first give the solution is sb.tostring () and then add Intern (), the next part of the reason, because I want to do a summary of string, and deepen the understanding of string.

OK, this code changes:

1  Public classStringthreadImplementsRunnable {2 3     Private Static FinalString lock_prefix = "XXX---";4     5     PrivateString IP;6     7      Publicstringthread (String IP) {8          This. IP =IP;9     }Ten  One @Override A      Public voidrun () { -          -String lock =Buildlock (); the         synchronized(lock) { -System.out.println ("[" + jdkutil.getthreadname () + "] started running"); -             //Hibernate 5-Second impersonation script call -Jdkutil.sleep (5000); +System.out.println ("[" + jdkutil.getthreadname () + "] End ran"); -         } +     } A      at     PrivateString Buildlock () { -StringBuilder SB =NewStringBuilder (); - sb.append (lock_prefix); - sb.append (IP); -          -String lock =sb.tostring (). Intern (); inSystem.out.println ("[" + jdkutil.getthreadname () + "] built the lock [" + Lock + "]"); -          to         returnlock; +     } -      the}

Take a look at the code execution results:

 [Thread-0] built the lock [XXX---192.168.1.1][thread -0  "started running [Thread -3] built the lock [XXX---192.168.1.1][ Thread -4] built the lock [xxx---192.168.1.1][thread -1] built the lock [XXX---192.168.1.1< Span style= "COLOR: #000000" >][thread -2] built the lock [XXX---192.168.1.1][thread  -0 "ends Running" thread  -2 "starts running [thread  -2 "End runs" thread  -1 "starts running [thread  -1" end runs [thread  -4] starts running [Thread -4  "ends Running" thread  -3 "starts running [thread  -3  

You can compare the results of the above without the Intern () method, it is obvious that 5 threads get the same lock, a thread executes the code inside the synchronized code block after the next thread to execute, the entire execution is serial.

Look at string again.

there is a constant pool in the JVM memory area, about the allocation of constant pools :

    1. JDK6 version, constant pool allocated in persistent generation PermGen
    2. JDK7 version, the constant pool is allocated in heap

Strings are stored in a constant pool, and two types of string data are stored in a constant pool:

    1. The compile time can be determined by the string, that is, the use of "" caused by the string, such as String a = "123",string b = "1" + b.getstringdatafromdb () + "2" + C.getstringdata Fromdb (), "123", "1", and "2" are all strings that can be determined during compilation, so they are placed in a constant pool, and B.getstringdatafromdb (), C.getstringdatafromdb () These two data are not determined during compilation, so they are allocated on the heap
    2. A string that is manipulated using the Intern () method of string, such as string B = B.getstringdatafromdb (). Intern (), although B.getstringdatafromdb () The string that the method gets is allocated on the heap, but because intern () is added later, the result of the B.getstringdatafromdb () method is written to the constant pool

The string data in a constant pool has one feature: whenever data is taken, if there is a constant pool, the data in the constant pool is taken directly, and if not in the constant pool, the data is written to the constant pool and the data in the constant pool is returned .

So back to our previous scenario, using the StringBuilder stitching string returns a new object each time, but using the Intern () method is different:

The string "XXX-192.168.1.1" was created using the ToString () method of StringBuilder, but because the Intern () method was used, the first thread found no "XXX-192.168.1.1" in the constant pool, In the usual amount of pool put a
"XXX-192.168.1.1", the following thread discovers that there is "XXX-192.168.1.1" in the constant pool, and takes the "XXX-192.168.1.1" in the constant pool directly.

So no matter how many threads, just take "XXX-192.168.1.1", it must be the same object, is the constant pool of "XXX-192.168.1.1"
All of this is the function of the Intern () method of the string

Postscript

To solve this problem, including this article finished, I have a little bit of emotion, many people will think a Java programmer can use the framework, can write the code flow without bugs, research the underlying principle, virtual machine what the fundamental is useless. I do not know whether this question can give us a little inspiration:

This business scenario is not complicated, and the entire code implementation is not very complex, but it has concurrency problems when it runs. If there is no solid foundation: Know the string in addition to those commonly used methods IndexOf, subString, concat and a very uncommon intern () method does not understand a little JVM:JVM memory distribution, In particular, the constant pool does not look at the JDK Source: StringBuilder's ToString () method does not have some understanding of concurrency: how to ensure that multithreading is a code block in the serial code block synchronized the issue of the problem, is simply impossible to solve, even can say how to do the analysis do not know.

Therefore, and do not feel that the JVM, JDK source code underlying implementation of the principle of what is useless, on the contrary, these are the technical personnel to grow up the most valuable things.

Remember once synchronized lock string thrown by the pit and then talk about the Java string

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.