>3.3使用并发容器ConcurrentHashMap设计一个缓存:

>>3.3.1缓存初步(简陋版本)

对于复杂耗时的操作,我们并不希望每次都从头开始地进行计算,可以通过缓存的方式提升性能:

public class HardWork {// HashMap 是线程不安全的,需要加synchronized,防止缓存被多次填充(也可以在LsCache方法加锁)// 注意,这样虽然保证了线程安全,不过同时只有一个线程可以执行work() 方法,对性能不利。如果造成多个等待,甚至不如不使用缓存public synchronized int work(int i) {if (LsCache.hasKey(i)) {return LsCache.popCache(i);}try {// 模拟耗时操作Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}int result = i << 3;// 第一次运算结果加缓存LsCache.addCache(i, result);return result;}
}
/*** 写一个基本的缓存类*/
public class LsCache {public static Map<Integer, Integer> map = new HashMap<Integer, Integer>();public static void addCache(Integer key, Integer value) {map.put(key, value);}public static Integer popCache(Integer key) {return map.get(key);}public static boolean hasKey(Integer key) {return map.containsKey(key);}
}
public class Go {public static void main(String[] args) {// 单线程模式// HardWork hw = new HardWork();// int result = hw.work(2);// int result2 = hw.work(2);// System.out.println(result);// System.out.println(result2);// 多线程模式Long begin = System.currentTimeMillis();HardWork hw = new HardWork();for (int i = 0; i < 2000; i++) {new Thread(new Runnable() {@Overridepublic void run() {int result = hw.work(2);System.out.println(result);}}).start();}while (Thread.activeCount() > 1) {}Long end = System.currentTimeMillis();Long result = end - begin;System.out.println("耗时" + result);}
}

运行结果:

>>3.3.2使用ConcurrentHashMap替换HashMap(改进版本)

由于ConcurrentHashMap本身就是并发容器,则可以去掉work() 方法的锁。更改为ConcurrentHashMap可以提高并发性,因为其锁模型的粒度更小一些。

我们加缓存想要的效果,是A过来问我1+1等于多少,我算过之后存入缓存,之后BCD...再问这个问题,我就直接去读缓存。

但是现在work() 方法没了锁之后,可能A、B同时进来方法,判断的时候都还没有缓存,这就导致同样的工作做了两遍。

现在我们想要,虽然多个线程同时进入work(i相同),如何保证我还算一次(降低CPU负载)?

放缓存的时候放 Future,我在算之前一开始就先放缓存(基本上没有耗时,很短,这个期间内并发量比较少,避免CPU重算)

public class HardWork {public int work(final int i) {Integer result = null;if (LsCache.hasKey(i)) {// 如果有缓存try {result = LsCache.popCache(i).get();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();}return result;}// 如果没有缓存Callable<Integer> task = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {Thread.sleep(5000);return i << 3;}};FutureTask<Integer> future = new FutureTask<>(task);// 马上加缓存,future还没算LsCache.addCache(i, future);// 开线程算futurenew Thread(future).start();try {result = future.get();} catch (InterruptedException e) {// 2、future是一个未来,我们要避免缓存“污染”,一旦出错要从缓存中移除\LsCache.removeValue(i);e.printStackTrace();} catch (ExecutionException e) {LsCache.removeValue(i);e.printStackTrace();}return result;}
}
public class LsCache {public static ConcurrentHashMap<Integer, Future<Integer>> map = new ConcurrentHashMap<Integer, Future<Integer>>();public static void addCache(Integer key, Future<Integer> value) {// 1、将put改为原子性操作,避免多个线程同时putmap.putIfAbsent(key, value);// 2、future是一个未来,我们要避免缓存“污染”,一旦出错要从缓存中移除}public static Future<Integer> popCache(Integer key) {return map.get(key);}public static boolean hasKey(Integer key) {return map.containsKey(key);}public static void removeValue(Integer key) {map.remove(key);}
}
public class Go {public static void main(String[] args) {// 单线程模式// HardWork hw = new HardWork();// int result = hw.work(2);// int result2 = hw.work(2);// System.out.println(result);// System.out.println(result2);// 多线程模式Long begin = System.currentTimeMillis();HardWork hw = new HardWork();for (int i = 0; i < 2000; i++) {new Thread(new Runnable() {@Overridepublic void run() {int result = hw.work(2);System.out.println(result);}}).start();}while (Thread.activeCount() > 1) {}Long end = System.currentTimeMillis();Long result = end - begin;System.out.println("耗时" + result);}
}

Java 并发(JUC 包-03)相关推荐

  1. Java并发编程包中atomic的实现原理

    转载自   Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿,作者[林湾村龙猫]最近在阅读Java源码,这一篇是他关于并发包中atomic类的源码阅读的总结.Hollis做了一点点修 ...

  2. JAVA并发类包介绍

    JAVA并发Apl介绍合集 1. java.util.concurrent包 1.1 Executors线程池 1.2 Queues队列 1.3 Concurrent Collections 1.4 ...

  3. Java多线程 -- JUC包源码分析2 -- Copy On Write/CopyOnWriteArrayList/CopyOnWriteArraySet

    本人新书出版,对技术感兴趣的朋友请关注: https://mp.weixin.qq.com/s/uq2cw2Lgf-s4nPHJ4WH4aw 上1篇讲述了Java并发编程的第1个基本思想–CAS/乐观 ...

  4. java并发-JUC

    CAS 无锁,乐观锁,自旋锁,轻量级锁 定义 Compare and Swap,是基于硬件级别的指令实现的同步原语,Java并发包java.utile.concurrent许多同步类基于CAS构建 c ...

  5. 【Java 并发编程】 03 万恶的 Bug 起源—并发编程的三大特性

    今天让我们一起走进并发编程中万恶的Bug起源-并发编程中三大特性.今天学习目标如下: 并发编程的三大特性都要哪些 ? 并发编程三大特性的由来? 如何解决并发编程三大特性问题? 基本概念 原子性:一组操 ...

  6. Java并发JUC(java.util.concurrent)集合不安全

  7. 6.juc包下的原子类AtomicInteger,AtomicLong等AtomicXXX介绍

     在介绍juc中的原子类之前,先看看官方文档对java.util.concurrent.atomic包的介绍官方文档地址这里截取翻译之后的部分描述 1. 支持对单个变量进行无锁线程安全编程 2. 类的 ...

  8. 多线程十 JUC包下的常用工具类

    JUC包下的常用工具类 1. CountDownLatch-闭锁 2. CyclicBarrier-循环栅栏 3. Semaphore-信号量 4. Exchanger-线程数据交换器 这篇文章主要是 ...

  9. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对"公平锁"的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括: 基本概念 ReentrantLock数据结构 参考代码 获取公平锁(基于JDK1.7.0 ...

最新文章

  1. SAP WM 业务部门Unplanned工单消耗导致WM层面单据异常问题之分析
  2. 【音频处理】Polyphone 样本编辑 和 样本工具 ( 波形图 | 信息 | 频率分析 | 均衡器 | 播放器 | 终点裁剪 | 自动循环节 | 空白移除 | 音量 平衡 音调 调整 )
  3. SQLite3单例模式(C++)
  4. 「LibreOJ NOIP Round #1」旅游路线
  5. poi为什么所有celltype都是string_不是所有向日葵都向阳,你知道为什么吗
  6. svm算法 java实现_SVM算法实现(一)
  7. RHEL7 本地yum源配置
  8. java CMS gc解析
  9. 用printf 输出各种数据格式(包含示例代码)
  10. img图片在父元素中居中的方法
  11. C++类的实现 奥特曼打怪兽
  12. SendMessage功能(中规中矩)
  13. NGS可变剪切之STAR+rmats软件使用
  14. 神经网络之 CNN 与 RNN 再梳理
  15. JZ2440开发板学习------中级(二十七)
  16. 初尝Google Code
  17. 电脑计算机和用户区分,电脑32位和64位的区别是什么
  18. css框架bootstrap ie,前端开发必备的10个Bootstrap工具
  19. 【FPGA】04_关于“复位“的理解与总结
  20. android 手表按钮事件,关于android-studio:上手做一个华为鸿蒙手表应用-4-生命周期事件...

热门文章

  1. Golang 五种原子性操作的用法详解
  2. laravel 创建自定义的artisan make命令来新建类文件
  3. 简易版Dubbo方法级性能监控(实现TP90、TP99)
  4. leetcode__Longest Substring Without Repeating Characters
  5. 什么是base64编码
  6. MySQL的主从复制详解
  7. 为什么我不推荐你使用vue-cli创建脚手架?
  8. ES6中的迭代器(Iterator)和生成器(Generator)(一)
  9. Spring Framework 官方文档学习(三)之Resource
  10. Java 联系Oracle 数据库