Java实现一个简单的缓存方法

Java实现一个简单的缓存方法

 

缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率。下面我实现了一个简单的缓存,步骤如下。

创建缓存对象EntityCache.java

 

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

29

30

31

32

33

34

35

36

37

38

39

40

41

42

public class EntityCache {

  /**

   * 保存的数据

   */

  private Object datas;

 

  /**

   * 设置数据失效时间,为0表示永不失效

   */

  private long timeOut;

 

  /**

   * 最后刷新时间

   */

  private long lastRefeshTime;

 

  public EntityCache(Object datas, long timeOut, long lastRefeshTime) {

    this.datas = datas;

    this.timeOut = timeOut;

    this.lastRefeshTime = lastRefeshTime;

  }

  public Object getDatas() {

    return datas;

  }

  public void setDatas(Object datas) {

    this.datas = datas;

  }

  public long getTimeOut() {

    return timeOut;

  }

  public void setTimeOut(long timeOut) {

    this.timeOut = timeOut;

  }

  public long getLastRefeshTime() {

    return lastRefeshTime;

  }

  public void setLastRefeshTime(long lastRefeshTime) {

    this.lastRefeshTime = lastRefeshTime;

  }

 

 

}

定义缓存操作接口,ICacheManager.java

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

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

public interface ICacheManager {

  /**

   * 存入缓存

   * @param key

   * @param cache

   */

  void putCache(String key, EntityCache cache);

 

  /**

   * 存入缓存

   * @param key

   * @param cache

   */

  void putCache(String key, Object datas, long timeOut);

 

  /**

   * 获取对应缓存

   * @param key

   * @return

   */

  EntityCache getCacheByKey(String key);

 

  /**

   * 获取对应缓存

   * @param key

   * @return

   */

  Object getCacheDataByKey(String key);

 

  /**

   * 获取所有缓存

   * @param key

   * @return

   */

  Map<String, EntityCache> getCacheAll();

 

  /**

   * 判断是否在缓存中

   * @param key

   * @return

   */

  boolean isContains(String key);

 

  /**

   * 清除所有缓存

   */

  void clearAll();

 

  /**

   * 清除对应缓存

   * @param key

   */

  void clearByKey(String key);

 

  /**

   * 缓存是否超时失效

   * @param key

   * @return

   */

  boolean isTimeOut(String key);

 

  /**

   * 获取所有key

   * @return

   */

  Set<String> getAllKeys();

}

实现接口ICacheManager,CacheManagerImpl.java

这里我使用了ConcurrentHashMap来保存缓存,本来以为这样就是线程安全的,其实不然,在后面的测试中会发现它并不是线程安全的。

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

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

public class CacheManagerImpl implements ICacheManager {

  private static Map<String, EntityCache> caches = new ConcurrentHashMap<String, EntityCache>();

 

  /**

   * 存入缓存

   * @param key

   * @param cache

   */

  public void putCache(String key, EntityCache cache) {

    caches.put(key, cache);

  }

 

  /**

   * 存入缓存

   * @param key

   * @param cache

   */

  public void putCache(String key, Object datas, long timeOut) {

    timeOut = timeOut > 0 ? timeOut : 0L;

    putCache(key, new EntityCache(datas, timeOut, System.currentTimeMillis()));

  }

 

  /**

   * 获取对应缓存

   * @param key

   * @return

   */

  public EntityCache getCacheByKey(String key) {

    if (this.isContains(key)) {

      return caches.get(key);

    }

    return null;

  }

 

  /**

   * 获取对应缓存

   * @param key

   * @return

   */

  public Object getCacheDataByKey(String key) {

    if (this.isContains(key)) {

      return caches.get(key).getDatas();

    }

    return null;

  }

 

  /**

   * 获取所有缓存

   * @param key

   * @return

   */

  public Map<String, EntityCache> getCacheAll() {

    return caches;

  }

 

  /**

   * 判断是否在缓存中

   * @param key

   * @return

   */

  public boolean isContains(String key) {

    return caches.containsKey(key);

  }

 

  /**

   * 清除所有缓存

   */

  public void clearAll() {

    caches.clear();

  }

 

  /**

   * 清除对应缓存

   * @param key

   */

  public void clearByKey(String key) {

    if (this.isContains(key)) {

      caches.remove(key);

    }

  }

 

  /**

   * 缓存是否超时失效

   * @param key

   * @return

   */

  public boolean isTimeOut(String key) {

    if (!caches.containsKey(key)) {

      return true;

    }

    EntityCache cache = caches.get(key);

    long timeOut = cache.getTimeOut();

    long lastRefreshTime = cache.getLastRefeshTime();

    if (timeOut == 0 || System.currentTimeMillis() - lastRefreshTime >= timeOut) {

      return true;

    }

    return false;

  }

 

  /**

   * 获取所有key

   * @return

   */

  public Set<String> getAllKeys() {

    return caches.keySet();

  }

}

CacheListener.java,监听失效数据并移除。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class CacheListener{

  Logger logger = Logger.getLogger("cacheLog");

  private CacheManagerImpl cacheManagerImpl;

  public CacheListener(CacheManagerImpl cacheManagerImpl) {

    this.cacheManagerImpl = cacheManagerImpl;

  }

 

  public void startListen() {

    new Thread(){

      public void run() {

        while (true) {

          for(String key : cacheManagerImpl.getAllKeys()) {

            if (cacheManagerImpl.isTimeOut(key)) {

             cacheManagerImpl.clearByKey(key);

             logger.info(key + "缓存被清除");

           }

          }

        }

      }

    }.start();

 

  }

}

测试类TestCache.java

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

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

public class TestCache {

  Logger logger = Logger.getLogger("cacheLog");

  /**

   * 测试缓存和缓存失效

   */

  @Test

  public void testCacheManager() {

    CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();

    cacheManagerImpl.putCache("test", "test", 10 * 1000L);

    cacheManagerImpl.putCache("myTest", "myTest", 15 * 1000L);

    CacheListener cacheListener = new CacheListener(cacheManagerImpl);

    cacheListener.startListen();

    logger.info("test:" + cacheManagerImpl.getCacheByKey("test").getDatas());

    logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest").getDatas());

    try {

      TimeUnit.SECONDS.sleep(20);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    logger.info("test:" + cacheManagerImpl.getCacheByKey("test"));

    logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest"));

  }

 

  /**

   * 测试线程安全

   */

  @Test

  public void testThredSafe() {

    final String key = "thread";

    final CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();

    ExecutorService exec = Executors.newCachedThreadPool();

    for (int i = 0; i < 100; i++) {

      exec.execute(new Runnable() {

        public void run() {

            if (!cacheManagerImpl.isContains(key)) {

              cacheManagerImpl.putCache(key, 1, 0);

            } else {

              //因为+1和赋值操作不是原子性的,所以把它用synchronize块包起来

              synchronized (cacheManagerImpl) {

                int value = (Integer) cacheManagerImpl.getCacheDataByKey(key) + 1;

                cacheManagerImpl.putCache(key,value , 0);

              }

            }

        }

      });

    }

    exec.shutdown();

    try {

      exec.awaitTermination(1, TimeUnit.DAYS);

    } catch (InterruptedException e1) {

      e1.printStackTrace();

    }

 

    logger.info(cacheManagerImpl.getCacheDataByKey(key).toString());

  }

}

testCacheManager()输出结果如下:

1

2

3

4

5

6

7

8

9

10

11

12

2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager

信息: test:test

2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager

信息: myTest:myTest

2017-4-17 10:34:01 io.github.brightloong.cache.CacheListener$1 run

信息: test缓存被清除

2017-4-17 10:34:06 io.github.brightloong.cache.CacheListener$1 run

信息: myTest缓存被清除

2017-4-17 10:34:11 io.github.brightloong.cache.TestCache testCacheManager

信息: test:null

2017-4-17 10:34:11 io.github.brightloong.cache.TestCache testCacheManager

信息: myTest:null

testThredSafe()输出结果如下(选出了各种结果中的一个举例):

1

2

2017-4-17 10:35:36 io.github.brightloong.cache.TestCache testThredSafe

信息: 96

可以看到并不是预期的结果100,为什么呢?ConcurrentHashMap只能保证单次操作的原子性,但是当复合使用的时候,没办法保证复合操作的原子性,以下代码:

1

2

3

if (!cacheManagerImpl.isContains(key)) {

              cacheManagerImpl.putCache(key, 1, 0);

            }

多线程的时候回重复更新value,设置为1,所以出现结果不是预期的100。所以办法就是在CacheManagerImpl.java中都加上synchronized,但是这样一来相当于操作都是串行,使用ConcurrentHashMap也没有什么意义,不过只是简单的缓存还是可以的。或者对测试方法中的run里面加上synchronized块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。

转载于:https://www.jb51.net/article/111518.htm

作者:BrightLoong

 

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页