Java中String做為synchronized同步鎖


 

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

 

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

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

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

  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時間超長!!!

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

這里要引用強大的google-guava包,這個包不是一般的強大,是完全要把apache-commons*取締掉的節奏啊!!!

 

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

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

//TODO:something

}

 

該類對 intern 做了很多的優化,使用弱引用包裝了你傳入的字符串類型,所以,這樣就不會對內存造成較大的影響, 可以使用該類的 pool.intern(str) 來進行對字符串intern, 好了,這樣就解決了內存的問題了,那么我們使用了該優點,並且避免了內存占用問題,完美解決。但這種在分布式系統中會有問題

//類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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM