问题场景
使用 redis 做缓存, 在缓存中查询不到时要先加锁再去 db 中查, 这时用查询 redis 时的锁作为 key 进行加锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Override public <K, T> T findCache(K key, long timeout, TimeUnit unit, Class<T> clazz, Callable<T> loadBack) { T result = redisService.get(key, clazz); if (null != result) { logger.info("load cache ======== {}.", key); return result; } else { synchronized (pool.intern(key)) { result = redisService.get(key, clazz); if (null != result) { logger.info("load cache ======== {}.", key); return result; } result = doLoadBack(loadBack); if (result != null) { redisService.set(key, result, timeout, unit); } return result; } } }
|
不能直接用 String 对象作为锁
synchronizd 加锁是基于对象进行的, value内容相同的两个 String, 如果不是同一个对象, 期望的加锁行为就无法完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class NicoThread extends Thread { private String s; private NicoThread(String s) { this.s = s; } @Override public void run() { synchronized (s) { try { String name = Thread.currentThread().getName(); System.out.println(name + ": begin sleep"); Thread.sleep(2000); System.out.println(name + ": end sleep"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) {
String s1 = "niconiconi"; String s2 = new String("niconiconi");
new NicoThread(s1).start(); new NicoThread(s2).start();
} }
|
1 2 3 4 5 6
| // 输出 Thread-0: begin sleep Thread-1: begin sleep Thread-1: end sleep Thread-0: end sleep // 同步失败
|
使用 intern() 对象加锁?
value内容相同的两个 String, intern() 后得到的是 String 常量池中的同一个对象, 可以完成期望的锁操作
1 2
| // synchronized(s) {} synchronized(s.intern()) {}
|
intern() 的 gc 问题
在jdk7下慎用String.intern()作为synchronized的对象锁 - 绝望生鱼片 - 博客园
深入解析String#intern
频繁调用 intern() 可能产生内存问题, 为了解决这个问题, 可以自定义一个 ConcurrentHashMap<String, Object> 用来储存传入的 key 与实际锁的对象
guava 的 interner 实现
api文档
1 2 3
| private Interner<String> interner = Interners.<String>newWeakInterner();
synchronized(interner.intern(str)) {}
|
类似数据结构
guava 的 cache 是在内存中用 map 做缓存
https://google.github.io/guava/releases/23.5-jre/api/docs/
参考资料
When is it beneficial to flyweight Strings in Java? - Stack Overflow
multithreading - Synchronizing on String objects in Java - Stack Overflow
locking - Simple Java name based locks? - Stack Overflow