再上一个场景:我们自己写一个简单的数据库连接池,能够复用数据库连接,并且能在高并发情况下正常工作。

实现代码1:

package test;

import java.util.concurrent.ConcurrentHashMap;

public class ConnectionPool {

    private ConcurrentHashMap<String, Connection> pool = new ConcurrentHashMap<String, Connection>();

    public Connection getConnection(String key) {
Connection conn = null;
if (pool.containsKey(key)) {
conn = pool.get(key);
} else {
conn = createConnection();
pool.putIfAbsent(key, conn);
}
return conn;
} public Connection createConnection() {
return new Connection();
} class Connection {}
}

我们用了ConcurrentHashMap,这样就不必把getConnection方法置为synchronized(当然也可以用Lock),当多个线程同时调用getConnection方法时,性能大幅提升。

貌似很完美了,但是有可能导致多余连接的创建,推演一遍:

某一时刻,同时有3个线程进入getConnection方法,调用pool.containsKey(key)都返回false,然后3个线程各自都创建了连接。虽然ConcurrentHashMap的put方法只会加入其中一个,但还是生成了2个多余的连接。如果是真正的数据库连接,那会造成极大的资源浪费。

所以,我们现在的难点是:如何在多线程访问getConnection方法时,只执行一次createConnection。

结合之前Future模式的实现分析:当3个线程都要创建连接的时候,如果只有一个线程执行createConnection方法创建一个连接,其它2个线程只需要用这个连接就行了。再延伸,把createConnection方法放到一个Callable的call方法里面,然后生成FutureTask。我们只需要让一个线程执行FutureTask的run方法,其它的线程只执行get方法就好了。

上代码:

package test;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class ConnectionPool { private ConcurrentHashMap<String, FutureTask<Connection>> pool = new ConcurrentHashMap<String, FutureTask<Connection>>(); public Connection getConnection(String key) throws InterruptedException, ExecutionException {
FutureTask<Connection> connectionTask = pool.get(key);
if (connectionTask != null) {
return connectionTask.get();
} else {
Callable<Connection> callable = new Callable<Connection>() {
@Override
public Connection call() throws Exception {
return createConnection();
}
};
FutureTask<Connection> newTask = new FutureTask<Connection>(callable);
connectionTask = pool.putIfAbsent(key, newTask);
if (connectionTask == null) {
connectionTask = newTask;
connectionTask.run();
}
return connectionTask.get();
}
} public Connection createConnection() {
return new Connection();
} class Connection {
}
}

推演一遍:当3个线程同时进入else语句块时,各自都创建了一个FutureTask,但是ConcurrentHashMap只会加入其中一个。第一个线程执行pool.putIfAbsent方法后返回null,然后connectionTask被赋值,接着就执行run方法去创建连接,最后get。后面的线程执行pool.putIfAbsent方法不会返回null,就只会执行get方法。

在并发的环境下,通过FutureTask作为中间转换,成功实现了让某个方法只被一个线程执行。

就这么多吧,真是呕心沥血啊!!!哈哈

Future模式衍生出来的更高级的应用的更多相关文章

  1. 彻底理解Java的Future模式

    先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材.网上购买厨具比较方便,食材去超市买更放心. 实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材.所以,在主线程里面另起一个子线 ...

  2. 【并发编程】Future模式添加Callback及Promise 模式

    Future Future是Java5增加的类,它用来描述一个异步计算的结果.你可以使用 isDone 方法检查计算是否完成,或者使用 get 方法阻塞住调用线程,直到计算完成返回结果.你也可以使用  ...

  3. java Future 模式

    考慮這樣一個情況,使用者可能快速翻頁瀏覽文件中,而圖片檔案很大,如此在瀏覽到有圖片的頁數時,就會導致圖片的載入,因而造成使用者瀏覽文件時會有停頓 的現象,所以我們希望在文件開啟之後,仍有一個背景作業持 ...

  4. 闲谈Future模式-订蛋糕

    一. Future模式简介 Future有道翻译:n. 未来:前途:期货:将来时.我觉得用期货来解释比较合适.举个实际生活中例子来说吧,今天我女朋友过生日,我去蛋糕店准备给女朋友定个大蛋糕,超级大的那 ...

  5. 【并发编程】Future模式及JDK中的实现

    1.1.Future模式是什么 先简单举个例子介绍,当我们平时写一个函数,函数里的语句一行行同步执行,如果某一行执行很慢,程序就必须等待,直到执行结束才返回结果:但有时我们可能并不急着需要其中某行的执 ...

  6. Qt 事件系统浅析 (用 Windows API 描述,分析了QCoreApplication::exec()和QEventLoop::exec的源码)(比起新号槽,事件机制是更高级的抽象,拥有更多特性,比如 accept/ignore,filter,还是实现状态机等高级 API 的基础)

    事件系统在 Qt 中扮演了十分重要的角色,不仅 GUI 的方方面面需要使用到事件系统,Signals/Slots 技术也离不开事件系统(多线程间).我们本文中暂且不描述 GUI 中的一些特殊情况,来说 ...

  7. 并发模型(一)——Future模式

    多线程开发可以更好的发挥多核cpu性能,常用的多线程设计模式有:Future.Master-Worker.Guard Susperionsion.不变.生产者-消费者 模式: jdk除了定义了若干并发 ...

  8. 多线程设计模式 - Future模式

    Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用.这类似我们日常生活中的在线购物流程,带在购物网看着一件商品时可以提交表单,当订单完成后就可以在家里等待商品送货上门.或者说 ...

  9. 一个尖括号能干什么,画一个笑脸开始(为了支持交互,它又增添了JavaScript。HTML页面也越来越臃肿。于是CSS便诞生了。API和核心代码的出现使HTML能够访问更复杂的软件功能--支持更高级的交互和云服务集成。这就是今天的HTML5)

    一个尖括号 < 一个尖括号能干什么 < ? 你可以编出一顶帽子 <(:-p 或一张笑脸 :-> 再或者更直接一些 20世纪90年代初,html作为一种简单标记语言面世,用于在互 ...

随机推荐

  1. MFC:关联变量

    1. 对象(控制)变量(control) a. 数据类型:control 只能创建关联一次 b).    control 用来操控控件 c). 创建 control 变量:控件 -> 右击 -& ...

  2. 升级AndroidStudio3.4问题汇总

    1.Could not get unknown property 'bootClasspath' for object of type org.gradle.api.tasks.compile.Com ...

  3. The 19th Zhejiang University Programming Contest - H

    Princess Cjb is caught by Heltion again! Her knights Little Sub and Little Potato are going to Helti ...

  4. Exp4 恶意代码分析

    一.原理与实践说明 1. 实践目标 1.1 监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2 分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysin ...

  5. Ext.net按钮事件中使用Response.Redirect的一个问题

    今天遇到一个Response.Redirect问题 通过Ext.net按钮写发送事件时,连接中会多出一个&amp,如果后台不进行UrlEncode操作的话,就会出问题,可是很不幸的是,有些跳转 ...

  6. Python包中 __init__.py文件的作用

    原创连接 https://www.cnblogs.com/AlwinXu/p/5598543.html Python包中 __init__.py文件的作用 在创建python包的过程中,IDE都会在包 ...

  7. VMware Workstation 常见问题解决

    本文以FAQ的方式进行整理,大家可以根据关键字进行查找即可. 问题一:VMware 安装64位操作系统报错“此主机支持Intel VT-x, 但Intel VT-x处于禁用状态” 问题二:This v ...

  8. CAD快捷键

    F1: 获取帮助 F2: 实现作图窗和文本窗口的切换 F3: 控制是否实现对象自动捕捉 F4:数字化仪控制 F5: 等轴测平面切换 F6: 控制状态行上坐标的显示方式 F7: 栅格显示模式控制 F8: ...

  9. cocos2d-x入门学习笔记——Hello world分析

    Hello world分析 1. “resource”文件夹 用于存放图片.音频和配置等资源文件.为了方便管理,可以创建在其中创建子文件夹.Cocos2d-x为我们屏蔽了不同平台对于文件路径的定义. ...

  10. vue组件创建的三种方式

    1.使用Vue.extend创建全局的Vue组件 //1.1 使用vue.extend创建组件 var com1 = Vue.extend({ //通过template属性指定组件要展示的html结构 ...