`

Java 高并发缓存与Guava Cache

 
阅读更多

一.背景

 

      缓存是我们在开发中为了提高系统的性能,把经常的访问业务的数据第一次把处理结果先放到缓存中,第二次就不用在对相同的业务数据在重新处理一遍,这样就提高了系统的性能。缓存分好几种:

 

(1)本地缓存。

 

(2)数据库缓存。

 

(3)分布式缓存。

 

      分布式缓存比较常用的有memcached等,memcached是高性能的分布式内存缓存服务器,缓存业务处理结果,减少数据库访问次数和相同复杂逻辑处理的时间,以提高动态Web应用的速度、 提高可扩展性

 

   

 

二.本地缓存在高并发下的问题以及解决

 

     今天我们介绍的是本地缓存缓存,我们这边采用java.util.concurrent.ConcurrentHashMap来保存,ConcurrentHashMap是一个线程安全的HashTable,并提供了一组和HashTable功能相同但是线程安全的方法,ConcurrentHashMap可以做到读取数据不加锁,提高了并发能力。我们先不考虑内存元素回收或者在保存数据会出现内存溢出的情况,我们用ConcurrentHashMap模拟本地缓存,当在高并发环境一下,会出现一些什么问题?

 


 

我们这边采用实现多个线程来模拟高并发场景。

 


 

 第一种:我们先来看一下代码:

 

[java] view plain copy
  1. public class TestConcurrentHashMapCache<K,V> {  
  2.     private final ConcurrentHashMap<K, V>  cacheMap=new ConcurrentHashMap<K,V> ();   
  3.       
  4.     public  Object getCache(K keyValue,String ThreadName){  
  5.         System.out.println("ThreadName getCache=============="+ThreadName);  
  6.         Object value=null;  
  7.         //从缓存获取数据  
  8.         value=cacheMap.get(keyValue);  
  9.         //如果没有的话,把数据放到缓存  
  10.         if(value==null){  
  11.             return putCache(keyValue,ThreadName);  
  12.         }  
  13.         return value;  
  14.     }  
  15.       
  16.     public Object putCache(K keyValue,String ThreadName){  
  17.         System.out.println("ThreadName 执行业务数据并返回处理结果的数据(访问数据库等)=============="+ThreadName);  
  18.         //可以根据业务从数据库获取等取得数据,这边就模拟已经获取数据了  
  19.         @SuppressWarnings("unchecked")  
  20.         V value=(V) "dataValue";  
  21.         //把数据放到缓存  
  22.          cacheMap.put(keyValue, value);  
  23.         return value;  
  24.     }  
  25.   
  26.       
  27.       
  28.     public static void main(String[] args) {  
  29.         final TestConcurrentHashMapCache<String,String> TestGuaVA=new TestConcurrentHashMapCache<String,String>();  
  30.           
  31.         Thread t1=new Thread(new Runnable() {  
  32.             @Override  
  33.             public void run() {  
  34.                   
  35.                 System.out.println("T1======start========");  
  36.                 Object value=TestGuaVA.getCache("key","T1");  
  37.                 System.out.println("T1 value=============="+value);  
  38.                 System.out.println("T1======end========");  
  39.                   
  40.             }  
  41.         });  
  42.           
  43.         Thread t2=new Thread(new Runnable() {  
  44.             @Override  
  45.             public void run() {  
  46.                 System.out.println("T2======start========");  
  47.                 Object value=TestGuaVA.getCache("key","T2");  
  48.                 System.out.println("T2 value=============="+value);  
  49.                 System.out.println("T2======end========");  
  50.                   
  51.             }  
  52.         });  
  53.           
  54.         Thread t3=new Thread(new Runnable() {  
  55.             @Override  
  56.             public void run() {  
  57.                 System.out.println("T3======start========");  
  58.                 Object value=TestGuaVA.getCache("key","T3");  
  59.                 System.out.println("T3 value=============="+value);  
  60.                 System.out.println("T3======end========");  
  61.                   
  62.             }  
  63.         });  
  64.           
  65.         t1.start();  
  66.         t2.start();  
  67.         t3.start();  
  68.   
  69.     }  
  70.   
  71. }  

  我们看一下执行结果,如图所示:

 

 

     我们实现了本地缓存代码,我们执行一下结果,发现在多线程时,出现了在缓存里没有缓存时,会执行一样执行多次的业务数据并返回处理的数据,我们分析一下出现这种情况的:

 

   (1)当线程T1访问cacheMap里面有没有,这时根据业务到后台处理业务数据并返回处理数据,并放入缓存

 

   (2)当线程T2访问cacheMap里面同样也没有,也把根据业务到后台处理业务数据并返回处理数据,并放入缓存

 

   第二种:

 

    这样相同的业务并处理两遍,如果在高并发的情况下相同的业务不止执行两遍,这样这样跟我们当初做缓存不相符合,这时我们想到了Java多线程时,在执行获取缓存上加上Synchronized,代码如下:

 

  

 

[java] view plain copy
  1. public class TestConcurrentHashMapCache<K,V> {  
  2.     private final ConcurrentHashMap<K, V>  cacheMap=new ConcurrentHashMap<K,V> ();   
  3.       
  4.     public <span style="color:#ff0000;">synchronized </span>Object getCache(K keyValue,String ThreadName){  
  5.         System.out.println("ThreadName getCache=============="+ThreadName);  
  6.         Object value=null;  
  7.         //从缓存获取数据  
  8.         value=cacheMap.get(keyValue);  
  9.         //如果没有的话,把数据放到缓存  
  10.         if(value==null){  
  11.             return putCache(keyValue,ThreadName);  
  12.         }  
  13.         return value;  
  14.     }  
  15.       
  16.     public Object putCache(K keyValue,String ThreadName){  
  17.         System.out.println("ThreadName 执行业务数据并返回处理结果的数据(访问数据库等)=============="+ThreadName);  
  18.         //可以根据业务从数据库获取等取得数据,这边就模拟已经获取数据了  
  19.         @SuppressWarnings("unchecked")  
  20.         V value=(V) "dataValue";  
  21.         //把数据放到缓存  
  22.          cacheMap.put(keyValue, value);  
  23.         return value;  
  24.     }  
  25.   
  26.       
  27.       
  28.     public static void main(String[] args) {  
  29.         final TestConcurrentHashMapCache<String,String> TestGuaVA=new TestConcurrentHashMapCache<String,String>();  
  30.           
  31.         Thread t1=new Thread(new Runnable() {  
  32.             @Override  
  33.             public void run() {  
  34.                   
  35.                 System.out.println("T1======start========");  
  36.                 Object value=TestGuaVA.getCache("key","T1");  
  37.                 System.out.println("T1 value=============="+value);  
  38.                 System.out.println("T1======end========");  
  39.                   
  40.             }  
  41.         });  
  42.           
  43.         Thread t2=new Thread(new Runnable() {  
  44.             @Override  
  45.             public void run() {  
  46.                 System.out.println("T2======start========");  
  47.                 Object value=TestGuaVA.getCache("key","T2");  
  48.                 System.out.println("T2 value=============="+value);  
  49.                 System.out.println("T2======end========");  
  50.                   
  51.             }  
  52.         });  
  53.           
  54.         Thread t3=new Thread(new Runnable() {  
  55.             @Override  
  56.             public void run() {  
  57.                 System.out.println("T3======start========");  
  58.                 Object value=TestGuaVA.getCache("key","T3");  
  59.                 System.out.println("T3 value=============="+value);  
  60.                 System.out.println("T3======end========");  
  61.                   
  62.             }  
  63.         });  
  64.           
  65.         t1.start();  
  66.         t2.start();  
  67.         t3.start();  
  68.   
  69.     }  
  70.   
  71. }  

执行结果,如图所示:

   

 

    这样就实现了串行,在高并发行时,就不会出现了第二个访问相同业务,肯定是从缓存获取,但是加上Synchronized变成串行,这样在高并发行时性能也下降了。

 

 第三种:

 

    我们为了实现性能和缓存的结果,我们采用Future,因为Future在计算完成时获取,否则会一直阻塞直到任务转入完成状态和ConcurrentHashMap.putIfAbsent方法,代码如下:

    

[java] view plain copy
  1. public class TestFutureCahe<K,V> {  
  2.     private final ConcurrentHashMap<K, Future<V>>  cacheMap=new ConcurrentHashMap<K, Future<V>> ();   
  3.       
  4.     public   Object getCache(K keyValue,String ThreadName){  
  5.         Future<V> value=null;  
  6.         try{  
  7.             System.out.println("ThreadName getCache=============="+ThreadName);  
  8.             //从缓存获取数据  
  9.             value=cacheMap.get(keyValue);  
  10.             //如果没有的话,把数据放到缓存  
  11.             if(value==null){  
  12.                 value= putCache(keyValue,ThreadName);  
  13.                 return value.get();  
  14.             }  
  15.             return value.get();  
  16.                   
  17.         }catch (Exception e) {  
  18.         }  
  19.         return null;  
  20.     }  
  21.       
  22.       
  23.     public Future<V> putCache(K keyValue,final String ThreadName){  
  24. //      //把数据放到缓存  
  25.         Future<V> value=null;  
  26.         Callable<V> callable=new Callable<V>() {  
  27.                 @SuppressWarnings("unchecked")  
  28.                 @Override  
  29.                 public V call() throws Exception {  
  30.                     //可以根据业务从数据库获取等取得数据,这边就模拟已经获取数据了  
  31.                     System.out.println("ThreadName 执行业务数据并返回处理结果的数据(访问数据库等)=============="+ThreadName);  
  32.                     return (V) "dataValue";  
  33.                 }  
  34.             };  
  35.             FutureTask<V> futureTask=new FutureTask<V>(callable);  
  36.             value=cacheMap.putIfAbsent(keyValue, futureTask);  
  37.             if(value==null){  
  38.                 value=futureTask;  
  39.                 futureTask.run();  
  40.             }  
  41.         return value;  
  42.     }  
  43.       
  44.       
  45.   
  46.       
  47.       
  48.     public static void main(String[] args) {  
  49.         final TestFutureCahe<String,String> TestGuaVA=new TestFutureCahe<String,String>();  
  50.           
  51.         Thread t1=new Thread(new Runnable() {  
  52.             @Override  
  53.             public void run() {  
  54.                   
  55.                 System.out.println("T1======start========");  
  56.                 Object value=TestGuaVA.getCache("key","T1");  
  57.                 System.out.println("T1 value=============="+value);  
  58.                 System.out.println("T1======end========");  
  59.                   
  60.             }  
  61.         });  
  62.           
  63.         Thread t2=new Thread(new Runnable() {  
  64.             @Override  
  65.             public void run() {  
  66.                 System.out.println("T2======start========");  
  67.                 Object value=TestGuaVA.getCache("key","T2");  
  68.                 System.out.println("T2 value=============="+value);  
  69.                 System.out.println("T2======end========");  
  70.                   
  71.             }  
  72.         });  
  73.           
  74.         Thread t3=new Thread(new Runnable() {  
  75.             @Override  
  76.             public void run() {  
  77.                 System.out.println("T3======start========");  
  78.                 Object value=TestGuaVA.getCache("key","T3");  
  79.                 System.out.println("T3 value=============="+value);  
  80.                 System.out.println("T3======end========");  
  81.                   
  82.             }  
  83.         });  
  84.           
  85.         t1.start();  
  86.         t2.start();  
  87.         t3.start();  
  88.   
  89.     }  
  90.   
  91. }  

 

     线程T1或者线程T2访问cacheMap,如果都没有时,这时执行了FutureTask来完成异步任务,假如线程T1执行了FutureTask,并把保存到ConcurrentHashMap中,通过PutIfAbsent方法,因为putIfAbsent方法如果不存在key对应的值,则将valuekey加入Map,否则返回key对应的旧值。这时线程T2进来时可以获取Future对象,如果没值没关系,这时是对象的引用,等FutureTask执行完,在通过get返回。

 

    我们问题解决了高并发访问缓存的问题,可以回收元素这些,都没有,容易造成内存溢出,Google  Guava Cache在这些问题方面都做得挺好的,接下来我们介绍一下。

三.Google  Guava Cache的介绍和应用



     http://www.java2s.com/Code/Jar/g/Downloadguava1401jar.htm  下载对应的jar包


 

    Guava CacheConcurrentMap很相似,Guava Cache能设置回收,能解决在大数据内存溢出的问题,源代码如下:

 

  

public class TestGuaVA<K,V> {
private   Cache<K, V> cache=  CacheBuilder.newBuilder() .maximumSize(2).expireAfterWrite(10, TimeUnit.MINUTES).build(); 
public   Object getCache(K keyValue,final String ThreadName){
Object value=null;
try {
System.out.println("ThreadName getCache=============="+ThreadName);
//从缓存获取数据
value = cache.get(keyValue, new Callable<V>() {  
           @SuppressWarnings("unchecked")
public V call() {  
            System.out.println("ThreadName 执行业务数据并返回处理结果的数据(访问数据库等)=============="+ThreadName);
return (V) "dataValue";
           }  
       });  
} catch (ExecutionException e) {
e.printStackTrace();
}
return value;
}





public static void main(String[] args) {
final TestGuaVA<String,String> TestGuaVA=new TestGuaVA<String,String>();


Thread t1=new Thread(new Runnable() {
@Override
public void run() {

System.out.println("T1======start========");
Object value=TestGuaVA.getCache("key","T1");
System.out.println("T1 value=============="+value);
System.out.println("T1======end========");

}
});

Thread t2=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T2======start========");
Object value=TestGuaVA.getCache("key","T2");
System.out.println("T2 value=============="+value);
System.out.println("T2======end========");

}
});

Thread t3=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T3======start========");
Object value=TestGuaVA.getCache("key","T3");
System.out.println("T3 value=============="+value);
System.out.println("T3======end========");

}
});

t1.start();
t2.start();
t3.start();


}


}


说明:

    

 

   CacheBuilder.newBuilder()后面能带一些设置回收的方法:

 

     1maximumSize(long):设置容量大小,超过就开始回收。

 

     (2expireAfterAccess(long, TimeUnit):在这个时间段内没有被读/写访问,就会被回收。

 

     (3expireAfterWrite(long, TimeUnit):在这个时间段内没有被写访问,就会被回收 。

 

        (4)removalListener(RemovalListener):监听事件,在元素被删除时,进行监听。

 

执行结果,如图所示:

 

  

 

转http://blog.csdn.net/congcong68/article/details/41146295

分享到:
评论

相关推荐

    使用google guava 实现定时缓存功能

    在IT行业中,Google Guava库是一个非常强大的工具集,它为Java开发人员提供了一系列实用的集合、缓存、并发和I/O工具。本篇文章将详细探讨如何利用Guava库实现定时缓存功能,以提高应用的性能和效率。 首先,Guava...

    guava-cache.rar

    尽管分布式缓存如Redis在高并发和大规模场景下表现出色,但本地缓存在某些情况下仍然具有不可忽视的优势,例如低延迟、无网络开销以及对实时性要求高的场景。 Guava Cache的设计目标是简化缓存管理,并提供自动过期...

    Go-Localcaching-GuavaCache在Go中的部分的实现

    Guava Cache是Java领域广泛使用的高效缓存解决方案,但Go语言本身并未直接提供与Guava Cache类似的功能。然而,开发者可以借鉴Guava Cache的设计理念,使用Go的标准库或者其他第三方库来实现类似的本地缓存机制。`...

    java map 实现缓存技术

    在许多实际应用中,尤其是高性能和高并发的场景,Map常被用来实现缓存技术,以提高数据读取速度和系统性能。本文将深入探讨如何使用Java Map实现缓存技术,以及其中的关键知识点。 首先,让我们理解什么是缓存。...

    JetCache是一个基于Java的缓存系统封装提供统一的API和注解来简化缓存的使用

    JetCache 支持多种缓存实现,包括本地缓存(如 Guava Cache)和分布式缓存(如 Redis、Memcached),并且提供了丰富的配置选项,可以根据实际需求进行定制。 **核心特性** 1. **统一的 API 和注解**:JetCache ...

    java中的缓存技术

    Java中的缓存技术是软件开发中的重要组成部分,尤其是在高并发、大数据量的系统中,缓存能够显著提高系统的响应速度和整体性能。本篇将详细探讨Java中的缓存技术及其应用。 首先,缓存的基本原理是将频繁访问的数据...

    java缓存类

    在Java中,有许多库和框架提供了缓存功能,如Ehcache、Guava Cache以及Spring框架中的Cache Abstraction。本文将重点讨论Java缓存的基本概念、常用库及其工作原理。 首先,理解缓存的基本概念是非常重要的。缓存是...

    cache设置缓存数据,可直接运行

    7. **分布式缓存**:在高并发的场景下,单机缓存可能无法满足需求,这时会采用分布式缓存,如Redis。分布式缓存能够提供更强大的扩展性和高可用性,确保在多台服务器间共享数据,并且能够在某台服务器故障时仍能正常...

    实战Java高并发程序设计模式高清视频教程.zip

    在本套"实战Java高并发程序设计模式高清视频教程"中,我们将深入探讨Java平台上的高并发编程技术和设计模式,这对于任何希望构建可扩展、高效且健壮的后端服务的开发者来说,都是必不可少的知识。Java作为业界广泛...

    java 数据缓存

    总结来说,Java数据缓存涉及到多个层面,包括源码实现如Guava Cache和Ehcache,以及使用工具如Redis和Memcached。理解这些缓存机制的原理和实践,对于优化Java应用程序性能至关重要。开发者可以根据具体需求选择合适...

    Caffeine是一个基于Java8的高性能缓存库

    1. **性能提升**:Caffeine的性能通常优于Guava Cache,特别是在高并发场景下。 2. **API设计**:Caffeine的API更加简洁且易于使用,同时提供了更多的定制选项。 3. **Java 8兼容**:Caffeine是为Java 8设计的,...

    java缓存工具 SimpleCache_java_缓存_

    5. **扩展性**:作为一个优秀的缓存工具,SimpleCache 可能允许用户自定义缓存实现,或者与其他缓存解决方案(如 Ehcache、Guava Cache)集成。 6. **缓存失效通知**:当缓存中的数据被更新或删除时,SimpleCache ...

    Java高并发高性能分布式框架从无到有微服务架构设计说明.docx

    Java高并发高性能分布式框架的设计与微服务架构紧密相关,旨在构建可扩展、容错性强、技术选型灵活的系统。微服务架构的核心理念是将大型应用程序拆分为一系列小型、独立的服务,每个服务专注于特定的业务功能,通过...

    Java缓存技术深入了解

    - ** caffeine**: 是一个高性能的本地缓存库,设计灵感来源于Guava Cache,但提供了更优化的性能和配置选项。 3. **缓存策略** - LRU(Least Recently Used):最近最少使用的淘汰策略,当缓存满时,删除最近最少...

    JAVA缓存技术深入了解

    - **Guava Cache**:Google的Guava库提供了一个轻量级的缓存解决方案,适合于简单的本地缓存需求。它具有自动加载、过期、大小限制等功能。 - **Hibernate Second Level Cache**:当使用Hibernate进行ORM时,第二级...

    利用Java开发高性能、高并发Web应用

    使用Guava Cache或Spring Cache进行本地缓存管理,实现热点数据快速响应。 6. **负载均衡**:Nginx或Apache HTTP Server作为反向代理和负载均衡器,可以分散请求到不同的服务器,增强系统的可用性和扩展性。 7. **...

    公司用的缓存设计(源代码),基于JAVA的,可以接受10万并发

    在Java中,常见的缓存实现有 Ehcache、Guava Cache 和 Redis 等。这些缓存系统通常提供线程安全、自动过期、内存与磁盘结合等特性,以满足不同需求。 基于Java的缓存设计,通常会涉及以下关键知识点: 1. **并发...

    Java高并发高性能分布式框架从无到有微服务架构设计.doc

    Java 高并发高性能分布式框架从无到有微服务架构设计 微服务架构模式(Microservice Architect Pattern)是当前热门的架构模式之一,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户...

    java cache 简单应用

    2. **Guava Cache**:Google的Guava库提供了一个强大的`Cache`接口,支持自动过期、弱引用、软引用等高级特性。通过`LoadingCache`,我们可以配置一个自动加载新值到缓存中的功能,当缓存项不存在时,会调用预定义的...

Global site tag (gtag.js) - Google Analytics