Java uses String as a synchronization lock

Since String will be stored in the constant pool, we know that String is generally not used as a synchronization lock, considering two aspects

  1. We use String as a lock, and hope that it can be like Object, with different variables locked without affecting each other . However, sometimes two String objects may point to the same string in the constant pool, causing their locks to affect each other. An example is as follows. If in two classes, use string literal assignment to declare two String objects, and use synchronizedkeywords to lock the two String objects separately. Due to the string constant pool, the two String objects The string object points to the same memory area in the constant pool, so the two locking methods will affect each other

    There are two classes StringSyn1 and StringSyn2, each of which has a lock method

    package com.yogurt.test;
    
    import java.util.concurrent.TimeUnit;
    
    public class StringSyn1 {
          
          
    
    	private String lockString = "yogurt";
    
    	public void lock() {
          
          
    		synchronized (lockString) {
          
          
    			System.out.println("StringSyn1 get lock");
    			try {
          
          
    				TimeUnit.SECONDS.sleep(2);
    			} catch (InterruptedException e) {
          
          
    				e.printStackTrace();
    			}
    			System.out.println("StringSyn1 is about to exit");
    		}
    	}
    }
    
    package com.yogurt.test;
    
    import java.util.concurrent.TimeUnit;
    
    public class StringSyn2 {
          
          
    
    	private String lockString = "yogurt";
    
    	public void lock() {
          
          
    		synchronized (lockString) {
          
          
    			System.out.println("StringSyn2 get lock");
    			try {
          
          
    				TimeUnit.SECONDS.sleep(2);
    			} catch (InterruptedException e) {
          
          
    				e.printStackTrace();
    			}
    			System.out.println("StringSyn2 is about to exit");
    		}
    	}
    }
    

    Create another test class and run

    public static void main(String[] args) {
          
          
    		StringSyn1 syn1 = new StringSyn1();
    		StringSyn2 syn2 = new StringSyn2();
    		(new Thread(() -> syn1.lock())).start();
    		(new Thread(() -> syn2.lock())).start();
    }
    

    Insert picture description here

    You can see that syn1 and syn2 are printed in turn, because the lockString variables of the two classes are assigned through string literals. This way, the lockString variables of the two classes all point to the same string object in the constant pool, two The lock method of the class is synchronized on the same String object, so syn2 is blocked by syn1. **In actual development, it is difficult for us to know whether the String object used as a lock comes from the constant pool, and whether it is also used as a lock in other places. **Of course, if you change the part of lockString above to lockString in the private String lockString = new String("yogurt");two classes, they are two different String objects. You can see that the lock methods of the two classes do not affect each other. Syn2 did not wait until the end of syn1. carried out

    Insert picture description here

  2. When String has business meaning, we hope that strings with the same content will add the same lock . For example, using the order number to lock, we hope that a certain order number can only have one operation for that order number at a time. So as long as the order number is the same, a lock should be added. When an order number is being processed, multiple requests with the same order number should be queued up on this lock. This is what we expect, however, the string content is the same, and there is no guarantee that it is the same String object. As shown in the above example, if two string objects are all referenced from the constant pool, they are the same object, and the effect of the same content string plus the same lock can be achieved . But what if two string objects have the same content, but are two different objects? For example, use the new keyword above to create a string. Assuming that there are two requests coming at the same time, they carry the same order number, but the string object storing the order number is a different object. At this time, the lock will fail, which will cause a critical operation on the same order number at the same time .

Generally, only when the string has business meaning, will it be necessary to use String as a lock, that is, in the second case, it is necessary to use String as a lock. In the first case, an ordinary Object can be used as a lock.

So what is the solution? The easiest way is to use the characteristics of the string constant pool, call the String intern method, put the String object into the constant pool, and then use it as a lock, so as to ensure that string variables with the same content point to the same object Up.

However, Java's native intern will not automatically clean up the constant pool and may cause frequent GC.

Fortunately, there is an Interner in Google's guava package, which can be used to create a constant pool based on weak references to avoid GC problems. Examples of use are as follows

package com.yogurt.test;

import com.google.common.collect.Interner;
import com.google.common.collect.Interners;

public class StringSyn {
    
    

	private Interner<String> stringPool = Interners.newWeakInterner();

	/**
	 * 针对具体订单号,执行临界操作
	 * **/
	public void doCriticalOperation(String orderId) {
    
    
		synchronized (stringPool.intern(orderId)) {
    
    
			//do something
		}
	}
}

Guess you like

Origin blog.csdn.net/vcj1009784814/article/details/108982051