Interners详解

关于String

  大家都知道,String是final的,每次对它的操作都会产生新的String,这很大程度上是安全性的考虑,但是产生大量的String也是会有一些问题的,1.大量的String会对gc产生影响;2.两次 new String(“aa”)操作,产生的String不一样,如果用这两个去做synchronized(String)操作就达不到想要的效果,因为synchronized必须是对同一个对象进行加锁才有效果。

   有两种办法可以让两次 new String(“aa”)的String指向同一对象:java的intern方法和guava Interners。

   我们先来看一个不使用intern方法和Interners的例子:

public class InternsTest {
 
    public static void main(String[] args) {
        Interner<String> pool = Interners.newWeakInterner();
        for (int i = 0; i < 5; i++) {
            String lock=new String("lock");
            TestInterns testInterns = new TestInterns(pool,lock, i);
            Thread thread = new Thread(testInterns);
            thread.start();
        }
    }
}
 
class TestInterns implements Runnable {
    private String lock;
    private int out;
    private Interner<String> pool;
 
    public TestInterns(Interner<String> pool,String lock, int out) {
        this.lock = lock;
        this.out = out;
        this.pool=pool;
    }
 
    @Override
    public void run() {
        //重点
        synchronized (lock) {
            System.out.println("--"+out);
            System.out.println(out+lock);
            System.out.println();
        }
    }
}

结果:

--0
--1
0lock

--2
2lock

1lock

--3
3lock

--4
4lock

  可以看到,多线程环境下,执行结果出现了不一致,说明 synchronized (lock)没有达到理想的效果,这是因为每次的lock都是不一样的。

下边看一个使用了intern方法的例子:

将上边代码synchronized (lock) 换成synchronized (lock.intern()),这样的话,每次拿到的都是从常量池中拿到的lock引用,所以起到了对lock加锁的作用

结果:

--0
0lock

--1
1lock

--2
2lock

--3
3lock

--4
4lock

下边看一个使用了Interners的例子:

  将上边代码synchronized (lock) 换成synchronized (pool.intern(lock))

结果:

--0
0lock

--1
1lock

--2
2lock

--3
3lock

--4
4lock

可以看到,也起到了对lock加锁的作用。

总结:

1.在这里,intern方法和Interners都能达到对lock加锁的效果,只是区别是:(1)interns常量池有限,存储在hashtable中,数据多了之后,碰撞厉害,而且容易加重full gc负担 (2)Interners内部基于ConcurrentHashMap实现,而且可以设置引用类型,不会加重full gc负担,但有一个问题就是如果gc回收了存储在Interners里面的String,那么pool.intern(lock)可能也会返回不同的引用,总之,还是建议使用Interners,效率和内存使用率更高

2.关于对String进行加锁的场景,大家可以想象一下,上边的代码是这样一个场景:lock是一个订单号,这个订单号比较特殊,对这个订单相关的操作必须串行,其他的订单可以并行执行。在多线程情况下,我们就需要对这个特殊的订单号进行加锁,以保证对这个订单操作的串行化,但是我们每一个进来的请求(线程)肯定带的订单号(lock)都是不一样的对象,如果贸然synchronized(lock)操作,肯定就会出现上边第一种情况的问题,所以这个时候Interners和intern方法就有用了。

posted @ 2019-09-19 08:23  superChong  阅读(1115)  评论(0编辑  收藏  举报