- 缓存池在项目开发中时长用到,在查看了一下相关的资料,发现很多博客中对缓存池的实现有存在着一些漏洞,今天花了点时间梳理一下,用JDK1.5中并发包中的ReentrantReadWriteLock实现一个从缓存中获取数据的例子。本人技术有限,如果读者发现Bug,敬请指出。程序员不喜欢多说,直接上代码
- 代码
1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.concurrent.locks.ReadWriteLock; 4 import java.util.concurrent.locks.ReentrantReadWriteLock; 5 6 /** 7 * @author yuguojin 8 */ 9 public class ThreadCaChe { 10 11 private static Map<String, Object> cacheMap = new HashMap<String, Object>(); 12 13 private static ReadWriteLock lock = new ReentrantReadWriteLock(); 14 15 public static void main(String[] args) { 16 17 for (int i = 0; i < 100; i++) { 18 new Thread(new Runnable() { 19 20 @Override 21 public void run() { 22 String obj = (String) get("TestKey"); 23 System.out.println(obj); 24 25 } 26 }).start(); 27 28 } 29 30 } 31 32 public static Object get(String key) { 33 lock.readLock().lock(); // 先加读锁 34 Object value = null; 35 try { 36 value = cacheMap.get(key); 37 if (value == null) { // 若不存在cache中 38 39 // 让线程sleep 1秒方便测试 40 try { 41 Thread.sleep(1000); 42 } catch (InterruptedException e) { 43 e.printStackTrace(); 44 } 45 46 lock.readLock().unlock(); // 若果value为空 则释放掉读锁,让该线程获取写锁,而其他线程只能等待该写锁释放,才能在进读锁 47 lock.writeLock().lock(); // 加写锁 48 49 value = cacheMap.get(key); 50 try { 51 if (value == null) { 52 value = "Cache Data";// 查询数据库 ,从DB中获取数据 53 // 存入缓存中 54 cacheMap.put(key, value); 55 } 56 } finally { 57 lock.readLock().lock(); 58 lock.writeLock().unlock(); 59 } 60 } 61 62 } finally { 63 lock.readLock().unlock(); // 释放第一次获取的读锁 64 } 65 66 return value; 67 } 68 }
- 代码备注
1. 代码中我使用了一个HashMap模拟缓存,在获取cacheMap缓存中的数据时如果cacheMap中没有存在该值得时候就涉及到从数据库中同步数据到cacheMap中,这样对于cacheMap就涉及到读写操作;
2. 当多个线程同时获取同一个数据的时候就就涉及到线程安全和线程同步的问题;
- 代码中容易出现的问题
1. 代码13行中的lock必须是属性级别的变量,初级程序员容易把lock写成方法变量(这样就达不到lock的作用)
2. 代码49行需要在读取缓存数据不存在的时候,并且已经获得读锁的时候还需要在验证一次这个数据是否存在缓存中(解决高并发时多个线程同时净增读锁是,后产生的多次数据库查询操作)
3. 锁是占用资源的操作,最后必须把锁释放,所以使用的finally模块释放锁
4. 代码46/47和代码57/58中释放锁和获取锁的顺序是不可以改变的(因为)在java锁机制中读锁是可以升级为写锁,然而写锁是不可以降级为读锁的,所以必须先释放读锁在获取写锁,不然会被锁死