Java中String作爲synchronized同步鎖

 

  synchronized (("" + userId).intern()) {
            // TODO:something
   }

 

JVM內存區域裏面有一塊常量池,關於常量池的分配:java

  1. JDK6的版本,常量池在持久代PermGen中分配
  2. JDK7的版本,常量池在堆Heap中分配

字符串是存儲在常量池中的,有兩種類型的字符串數據會存儲在常量池中:apache

  1. 編譯期就能夠肯定的字符串,即便用""引發來的字符串,好比String a = "123"String b = "1" + B.getStringDataFromDB() + "2" + C.getStringDataFromDB()、這裏的"123"、"1"、"2"都是編譯期間就能夠肯定的字符串,所以會放入常量池,而B.getStringDataFromDB()、C.getStringDataFromDB()這兩個數據因爲編譯期間沒法肯定,所以它們是在堆上進行分配的
  2. 使用String的intern()方法操做的字符串,好比String b = B.getStringDataFromDB().intern(),儘管B.getStringDataFromDB()方法拿到的字符串是在堆上分配的,可是因爲後面加入了intern(),所以B.getStringDataFromDB()方法的結果,會寫入常量池中

常量池中的String數據有一個特色:每次取數據的時候,若是常量池中有,直接拿常量池中的數據;若是常量池中沒有,將數據寫入常量池中並返回常量池中的數據分佈式

這個在jdk6裏問題不算大,由於String.intern()會在perm裏產生空間,若是perm空間夠用的話,這個不會致使頻繁Full GC,優化

可是在jdk7裏問題就大了,String.intern()會在heap裏產生空間,並且仍是老年代,若是對象一多就會致使Full GC時間超長!!!ui

慎用啊!解決辦法?終於找到了。this

這裏要引用強大的google-guava包,這個包不是通常的強大,是徹底要把apache-commons*取締掉的節奏啊!!!google

 

Interner<String> pool = Interners.newWeakInterner();

synchronized ( pool.intern("BizCode"+userId)){

//TODO:something

}

 

該類對 intern 作了不少的優化,使用弱引用包裝了你傳入的字符串類型,因此,這樣就不會對內存形成較大的影響, 可使用該類的 pool.intern(str) 來進行對字符串intern, 好了,這樣就解決了內存的問題了,那麼咱們使用了該優勢,而且避免了內存佔用問題,完美解決。但這種在分佈式系統中會有問題spa

//類1- SynStringTest
package com.tinygao.thread.synstring;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SynStringTest {

    private final static SynString synStr = new SynString();
    private final static Stopwatch sw = Stopwatch.createStarted();
    private static BiConsumer<SynString, String> function = (x, y)->{
        synchronized (x.getStringLock(y)) {
            log.info("Get lock: {}", y);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    public static void main(String[] args) throws InterruptedException {
        final ExecutorService executorService = Executors.newFixedThreadPool(
                4,
                new ThreadFactoryBuilder().setNameFormat("SynString-%d").build()
        );
        
        executorService.submit(()->{
            doTask("test");
        });
        executorService.submit(()->{
            doTask("test");
        });
        executorService.submit(()->{
            doTask("test1");
        });
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        sw.stop();
    }
    
    private static void doTask(String lockStr) {
        function.accept(synStr, lockStr);
        log.info("Do get lockStr successed waste time elapsed : {} ms", sw.elapsed(TimeUnit.MILLISECONDS));
    }
}

//類2- SynString 
package com.tinygao.thread.synstring;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SynString {

    private static ConcurrentMap<String,Object> parMap =  Maps.newConcurrentMap();
    
    public  Object getStringLock(String string) {
        Object lock = this;
        if(parMap != null) {
            Object newLock = new Object();
            lock = parMap.putIfAbsent(string, newLock);
            if(lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }
    
    public static void main(String[] args) {
        Object result = parMap.putIfAbsent("h", "g");
        log.info("Get result: {}", result);
    }
}
相關文章
相關標籤/搜索