How to String based synchronization lock?

At some point, we may want to do something based on a string, for example: synchronization of concurrent users for the same, to achieve more rational use of the string lock mode. Because only in the case of the same string, concurrent operation is not allowed. And if we, regardless of indiscriminate direct all locked, then the overall performance will drop too much.

  Because of the variety of string, string locks looks natural is an advanced lock lock segment score more advantages like it.

      Because the variable of type String assignment is this: String a = "hello world."; All often have the wrong image, String objects is immutable.

  The amount, the debate on this issue we will not elaborate on, in short, "a"! = "A" is likely to be established.

  In addition, for locking it, we all know, is to lock for the same object, it will be meaningful. So rough, so we can use the string lock:

Copy the code
    
    public void method1() {
        String str1 = "a";
        synchronized (str1) {
            // do sync a things...
        }
    }
        
    public void method2() {
        String str2 = "a";
        synchronized (str2) {
            // do sync b things...
        }
    }
Copy the code

  At first glance, this is indeed a very convenient and simple. However, said earlier, "a" is probably not equal to "a" (which is in most cases, only when the String is stored in the same time when the constant pool String variables are equal).

  So, we can optimize slightly lower:

Copy the code
    public void method3() {
        String str1 = "a";
        synchronized (str1.intern()) {
            // do sync a things...
        }
    }

    public void method4() {
        String str2 = "a";
        synchronized (str2.intern()) {
            // do sync b things...
        }
    }
Copy the code

  Still it looks very convenient and simple, the principle is the String object into the constant pool. But there is a problem, these constant pool of data on how to clean it?

  Anyway, we can not go on their own to achieve a String lock it?

  It is certainly possible it! Directly on the code!

Copy the code
org.slf4j.Logger Import; 
Import org.slf4j.LoggerFactory; 

Import a java.util.concurrent.ConcurrentHashMap; 
Import java.util.concurrent.ConcurrentMap; 
Import java.util.concurrent.CountDownLatch; 

/ ** 
 * based on the string implementation lock 
 * / 
public class StringBasedMutexLock Final { 

    Private static Logger Logger = Final LoggerFactory.getLogger (StringBasedMutexLock.class); 

    / ** 
     * character lock manager, converting a string to each of a CountDownLatch 
     * 
     * i.e. occurs only on genuine lock concurrent updates under the same conditions a String 
     * 
     * / 
    Private static Final the ConcurrentMap <String, a CountDownLatch> lockKeyHolder of ConcurrentHashMap new new = <> (); 

    / **  
     * based lockKey locked, synchronous execution
     *
     * @Param lockKey character lock 
     * / 
    public static void Lock (String lockKey) { 
        the while {(tryLock (lockKey)!) 
            The try { 
                logger.debug ( "[characters] concurrent update lock lock, {}", lockKey); 
                blockOnSecondLevelLock (lockKey); 
            } the catch (InterruptedException E) { 
                Thread.currentThread () interrupt ();. 
                logger.error ( "[] characters lock interrupt exception:" + lockKey, E); 
                bREAK; 
            } 
        } 
    } 

    / ** 
     * release lockKey corresponding lock option that other threads executable 
     * 
     * @param lockKey be mutually exclusive string  
     * @return true: successfully released, false: failed release, other threads may be mistaken release
     * / 
    public static Boolean UNLOCK (string lockKey) { 
        // first remove the lock, and then releases the lock, the follow-up to come in here will lead to concurrent execution priority, no effect 
        CountDownLatch realLock = getAndReleaseLock1 (lockKey); 
        releaseSecondLevelLock (realLock); 
        return to true; 
    } 

    / ** 
     * try to specify the character string lock 
     * 
     * @param lockKey to exclusive use of the string 
     * @return true: unlocked successfully, false: failed locked 
     * / 
    Private static boolean tryLock (string lockKey) { 
        // here will lead to a large number of objects created ReentrantLock ? 
        // Actually no, this number is equal to the maximum number of concurrent outside, just gc not very friendly, will be repeated again and again to create the destruction of the y- 
        return lockKeyHolder.putIfAbsent (lockKey, new new CountDownLatch (1)) == null; 
    } 

    / ** 
     * level 1 release lock (deleted) and return to the heavyweight lock 
     *
     * @Param lockKey lock character  
     * @return true lock
     * / 
    Private static getAndReleaseLock1 a CountDownLatch (String lockKey) { 
        return lockKeyHolder.remove (lockKey); 
    } 

    / ** 
     * two locks (lock escalation) 
     * 
     * @param lockKey lock the string 
     * @throws InterruptedException Throws interrupt exception 
     * / 
    Private static void blockOnSecondLevelLock (String lockKey) throws InterruptedException { 
        CountDownLatch realLock = getRealLockByKey (lockKey); 
        // is null illustrate this point the lock has been removed, the Next Race 
        IF (realLock = null!) { 
            realLock. the await (); 
        } 
    } 

    / ** 
     * two lock unlocked (if necessary) 
     * 
     * @param realLock lock example 
     * / 
    Private static void releaseSecondLevelLock (a CountDownLatch realLock) { 
        realLock.countDown (); 
    } 

    / ** 
     * corresponding instance by acquiring the lock Key 
     * 
     * @param lockKey lock the string 
     * @return lock example 
     * / 
    Private static getRealLockByKey a CountDownLatch (String lockKey) { 
        return lockKeyHolder.get (lockKey); 
    } 

}
Copy the code

  When in use, you can simply pass lockKey.

    // lock 
    StringBasedMutexLock.lock (linkKey); 
    // unlock 
    StringBasedMutexLock.unlock (linkKey);
    

  What good is it doing this?

    1. ConcurrentHashMap implementation lock acquisition, performance is good;
    2. Each string corresponds to a lock, after use to complete the removal will not cause a memory overflow;
    3 may be used as an external tool, easy access service code without, like synchronized, need to wrap up the entire section of code;

  Inadequate?

    1. ConcurrentHashMap implementation lock acquisition, performance is good;
    2. Each string corresponds to a lock, after use to complete the removal will not cause a memory overflow;
    3 may be used as an external tool, easy access service code without as synchronized as the code needs to wrap up the whole;
    4. this article just want to show to achieve String lock, this lock is not suitable for parallel processing in a distributed scenario;

 

Expansion: If you do not use String lock, how to ensure the security thread a small probability of a large premise concurrent concurrent scene?

  We know CAS efficiency is relatively high, we can use the class atoms to operate the CAS.

  For example, we add a status field, manipulate this field to ensure thread safety:

Copy the code
    / ** 
     * operating state 
     * 
     * 4: Deleting, 1: is being put into the queue, 0: Normal operation None 
     * / 
    Private transient volatile runningStatus of AtomicInteger of AtomicInteger new new = (0); 
    
    
    to obtain the state // Update: 
    public method5 void () { 
        of AtomicInteger runningStatus link.getRunningStatus = (); 
        // process the data being deleted, waiting 
        IF (runningStatus.compareAndSet (0,. 1)!) { 
            // delete the complete thread waits for another 1. 
            // 2. remove updating identification 
            // 3. The data re-run into the logical 
            Long lockStartTime = System.currentTimeMillis (); 
            Long maxLockTime = 10 * 1000; 
            the while (! runningStatus.compareAndSet (0,. 1)) {
                IF (System.currentTimeMillis () - lockStartTime> maxLockTime) { 
                    BREAK; 
                } 
            } 
            runningStatus.compareAndSet (. 1, 0); 
            the throw a RuntimeException new new ( "data being updated, re-run:" link.getLinkKey + () + Link); 
        } 
        {the try 
            // do Sync Things 
        } 
        the finally { 
            runningStatus.compareAndSet (. 1, 0); 
        } 
    } 
    
    public of method6 void () { 
        of AtomicInteger runningStatus link.getRunningStatus = (); 
        IF (! runningStatus.compareAndSet (0,. 4)) { 
            Logger .error ( "data being updated, not deleted, return");
            return; 
        } 
        the try {
            // do sync things
        }
        the catch (Exception E) { 
            logger.error ( "concurrent update exception:", E); 
        } 
        the finally { 
            runningStatus.compareAndSet (. 4, 0); 
        } 
    }
    
Copy the code

  The actual test down, CAS performance is better than the performance of synchronized lock like to be good. Of course, the number of concurrent against us are few, we just want to ensure thread safety in these rare cases. So, in fact, better.

 

Nagging: calm down.

 

Do not be afraid of suffering today, tomorrow you have to believe, more bitter!
 
Tags:  JAVA concurrency locks String.intern CAS ConcurrenHashMap

Guess you like

Origin www.cnblogs.com/xichji/p/11576328.html