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

 

posted @ 2019-07-28 20:59  枫树湾河桥  阅读(1257)  评论(0编辑  收藏  举报
Live2D