- 浏览: 874838 次
- 性别:
- 来自: 北京
博客专栏
-
Java Concurre...
浏览量:97541
文章分类
最新评论
-
guo_guo_guo:
mark
Zookeeper 安装和配置 -
solrer:
xunux 写道windows下,dataDir目录若是用需要 ...
Zookeeper 安装和配置 -
kevlearnjava:
你好 我是按照你的伪集群进行配置的 然后启动第二个和第三个 ...
Zookeeper 安装和配置 -
筑浪小子:
博主应该把vector设定为全局变量,更加清晰一些
线程安全的集合类--Java Concurrency In Practice C05读书笔记 -
tt5753:
jps16437 QuorumPeerMain16663 Zo ...
Zookeeper 安装和配置
[本文是我对Java Concurrency In Practice 5.6的归纳和总结. 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
几乎每一个应用都会使用到缓存, 但是设计高效的线程安全的缓存并不简单. 如:
public interface Computable<A, V> { V compute(A arg) throws InterruptedException; } public class ExpensiveFunction implements Computable<String, BigInteger> { // 模拟一个耗时操作 public BigInteger compute(String arg) { // ... return new BigInteger(arg); } } public class Memorizer1<A, V> implements Computable<A, V> { private final Map<A, V> cache = new HashMap<A, V>(); private final Computable<A, V> c; public Memorizer1(Computable<A, V> c) { this.c = c; } // 使用synchronized同步整个方法解决线程安全 public synchronized V compute(A arg) throws InterruptedException { V result = cache.get(arg); if (result == null) { result = c.compute(arg); cache.put(arg, result); } return result; } }
Memorizer1使用HashMap缓存计算结果. 如果能在缓存中取出参数对应的结果, 就直接返回缓存的数据, 避免了重复进行代价昂贵的计算. 由于HashMap不是线程安全的, Memorizer1同步整个compute方法, 避免重复计算的同时, 牺牲了并发执行compute方法的机会, 此种设计甚至可能导致性能比没有缓存更差.
使用ConcurrentHashMap代替HashMap, 同时取消对compute方法的同步可以极大的改善性能:
public class Memorizer2<A, V> implements Computable<A, V> { private final Map<A, V> cache = new ConcurrentHashMap<A, V>(); private final Computable<A, V> c; public Memorizer2(Computable<A, V> c) { this.c = c; } public V compute(A arg) throws InterruptedException { V result = cache.get(arg); if (result == null) { result = c.compute(arg); cache.put(arg, result); } return result; } }
ConcurrentHashMap是线程安全的, 并且具有极好的并发性能. 但是该设计仍存在问题: 无法避免所有的重复的计算. 有时这是可以的, 但对于一些要求苛刻的系统, 重复计算可能会引发严重的问题. Memorizer2的问题在于一个线程在执行compute方法的过程中, 其他线程以相同的参数调用compute方法时, 无法从缓存中获知已有线程正在进行该参数的计算的信息, 因此造成了重复计算的发生. 针对这一点, 可以改进缓存的设计:
public class Memorizer3<A, V> implements Computable<A, V> { // 改为缓存Future private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); private final Computable<A, V> c; public Memorizer3(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { Future<V> f = cache.get(arg); if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; FutureTask<V> ft = new FutureTask<V>(eval); f = ft; // 在计算开始前就将Future对象存入缓存中. cache.put(arg, ft); ft.run(); // call to c.compute happens here } try { // 如果缓存中存在arg对应的Future对象, 就直接调用该Future对象的get方法. // 如果实际的计算还在进行当中, get方法将被阻塞, 直到计算完成 return f.get(); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } } }
Memorizer3中的缓存系统看起来已经相当完美: 具有极好的并发性能, 也不会存在重复计算的问题. 真的吗? 不幸的是Memorizer3仍然存在重复计算的问题, 只是相对于Memorizer2, 重复计算的概率降低了一些. cache.get(arg)的结果为null, 不代表cache.put(arg, ft)时cache中依旧没有arg对应的Future, 因此直接调用cache.put(arg, ft)是不合理的:
public class Memorizer<A, V> implements Computable<A, V> { private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); private final Computable<A, V> c; public Memorizer(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { while (true) { Future<V> f = cache.get(arg); if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; FutureTask<V> ft = new FutureTask<V>(eval); // 使用putIfAbsent测试是否真的将ft存入了缓存, 如果存入失败, 说明cache中已经存在arg对应的future对象 // 否则才进行计算. f = cache.putIfAbsent(arg, ft); if (f == null) { f = ft; ft.run(); } } try { return f.get(); } catch (CancellationException e) { // 当计算被取消时, 从缓存中移除arg-f键值对 cache.remove(arg, f); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } } } }
至此才真正实现了高效且线程安全的缓存.
PS: 终于看完了JCIP的第五章, 这一章真是又臭又长...
评论
最后获取的时候 f.get()和ft.get()是否也有区别呢?
是不是这样理解 ft.run()是在另一个线程中。f可以获取ft中运行的结果。
如果是ft.get的话会阻塞。
最后获取的时候 f.get()和ft.get()是否也有区别呢?
朋友, 你想表达什么? 是觉得我的叙述词不达意吗?
发表评论
-
状态依赖的类--JCIP C14.1读书笔记
2012-04-11 10:24 2665[本文是我对Java Concurrency In Pract ... -
内置锁和显式锁的区别--JCIP C13读书笔记
2012-04-11 10:17 5782[本文是我对Java Concurrenc ... -
改善并发程序的可扩展性--JCIP C11读书笔记
2012-04-10 14:40 2512[本文是我对Java Concurrency In Pract ... -
如何避免死锁--JCIPC10读书笔记
2012-04-10 10:08 3156[本文是我对Java Concurrency In Pract ... -
task与execution--JCIPC08读书笔记
2012-04-09 10:34 2402[本文是我对Java Concurrency In Pract ... -
配置ThreadPoolExecutor
2012-04-09 10:34 6236[本文是我对Java Concurrency In Pract ... -
停止基于线程的Service--JCIP7.2读书笔记
2012-04-06 10:28 2336[本文是我对Java Concurrency In Pract ... -
处理不可中断的阻塞-JCIP7.1读书笔记
2012-04-06 10:23 5703[本文是我对Java Concurrenc ... -
处理InterruptedException异常--JCIP7.1读书笔记
2012-04-05 14:08 6128[本文是我对Java Concurrency In Pract ... -
中断线程--JCIP7.1读书笔记
2012-04-05 14:03 2615[本文是我对Java Concurrency In Pract ... -
改善并发性能--JCIP6.3读书笔记
2012-04-02 11:51 2573[本文是我对Java Concurrency In Pr ... -
Executor--JCIP C06读书笔记
2012-04-02 09:28 2864[本文是我对Java Concurrency In Pract ... -
synchronizer--JCIP5.5读书笔记
2012-04-01 22:44 2348[本文是我对Java Concurrency In Pract ... -
使用BlockingQueue构建生产者消费者模式--JCIP5.3读书笔记
2012-03-31 17:32 4644[本文是我对Java Concurrency In Pract ... -
ConcurrentHashMap和CopyOnWriteArrayList--Java Concurrency In Practice C05读书笔记
2012-03-31 11:27 4043[本文是我对Java Concurrenc ... -
线程安全的集合类--Java Concurrency In Practice C05读书笔记
2012-03-28 18:26 14250[本文是我对Java Concurrency In Pract ... -
利用对象限制和委托构建线程安全的类--Java Concurrency In Practice C04读书笔记
2012-03-27 18:23 3830[本文是我对Java Concurrency In Pract ... -
变量可见性和volatile, this逃逸, 不可变对象, 以及安全公开--Java Concurrency In Practice C03读书笔记
2012-03-26 21:55 12585[本文是我对Java Concurrency In Pract ... -
变量可见性和volatile, this逃逸, 不可变对象, 以及安全公开--Java Concurrency In Practice C02读书笔记
2012-03-26 21:53 2[本文是我对Java Concurrency In Pract ... -
Race condition--Java Concurrency In Practice C02读书笔记
2012-03-26 10:17 4996[本文是我对Java Concurrenc ...
相关推荐
Java多线程与线程安全实践-基于Http协议的断点续传(毕业设计+课程设计)Java多线程与线程安全实践-基于Http协议的断点续传(毕业设计+课程设计)Java多线程与线程安全实践-基于Http协议的断点续传(毕业设计+课程...
javaweb毕业设计-Java多线程与线程安全实践-基于Http协议的断点续传(可做课程设计).rarjavaweb毕业设计-Java多线程与线程安全实践-基于Http协议的断点续传(可做课程设计).rarjavaweb毕业设计-Java多线程与线程安全...
Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与...
JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多线程与线程安全实践-基于Http协议的断点续传 JAVA多...
Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与线程安全实践-基于Http协议的断点续传Java多线程与...
基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于Java多线程与线程安全实践-基于Http协议的断点续传设计与实现.zip基于...
Java多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传.rarJava多线程与线程安全实践-基于Http协议的断点续传...
标题中的"php_redis-2.2.7-5.6-nts-vc11-x86"和"php_redis-2.2.5-5.6-ts-vc11-x86"指的是PHP的Redis扩展的不同版本,适用于PHP 5.6。这里的"2.2.7"和"2.2.5"是Redis扩展的版本号,"5.6"对应的是PHP的版本号。"nts"和...
安装"mysql-installer-community-5.6.35.0.msi"后,用户可以配置MySQL Server,包括设置root用户的密码、选择服务器类型(开发、生产等)、配置网络选项以及安装额外的组件,如MySQL Workbench(用于数据库设计和...
[计算机毕设]基于java的多线程与线程安全实践-基于Http协议的断点续传系统设计与实现(源代码).zip [计算机毕设]基于java的多线程与线程安全实践-基于Http协议的断点续传系统设计与实现(源代码).zip [计算机毕设]基于...
`php_redis-2.2.7-5.6-ts-vc11-x64.zip`文件包含了适用于PHP 5.6,线程安全(TS)版本,使用Visual C++ 11编译器,并且是为64位Windows系统编译的php_redis扩展。 其次,`php_igbinary`扩展则是用于替代默认的PHP...
MYSQL是一个多线程的,结构化查询语言(SQL)数据库服务器。SQL 在世界上是最流行的数据库语言。MySQL 的执行性能非常高,运行速度非常快,并非常容易使用。是一个非常捧的数据库。此为最新版本5.6.11
本文将深入探讨PHP的Redis扩展,特别是针对“php_redis-2.2.5-5.6-ts-vc11-x64”这一版本,它专为PHP 5.6版本、线程安全(TS)、Visual C++ 11编译器以及64位(x64)系统设计。 首先,我们来理解PHP Redis扩展的...
### Percona-XtraDB-Cluster-5.6 相关知识点详述 #### 一、简介 ##### 1.1 关于 Percona XtraDB Cluster Percona XtraDB Cluster (PXC) 是一个专为 MySQL 用户设计的高度可用性和可扩展性解决方案。它通过提供同步...
标题“php_redis-2.2.5-5.6-ts-vc11-x86”表明这是一个针对PHP的Redis扩展库,具体版本为2.2.5,适配PHP 5.6版本,且是线程安全(TS)版本,采用Visual C++ 11编译器(VC11)构建,适用于32位(x86)系统。...
MYSQL是一个多线程的,结构化查询语言(SQL)数据库服务器。SQL 在世界上是最流行的数据库语言。MySQL 的执行性能非常高,运行速度非常快,并非常容易使用。是一个非常捧的数据库。此为最新版本5.6.11