zoukankan      html  css  js  c++  java
  • Future模式衍生出来的更高级的应用

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

    实现代码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作为中间转换,成功实现了让某个方法只被一个线程执行。

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

  • 相关阅读:
    分享知识-快乐自己:Maven 相关原理
    分享知识-快乐自己:Struts2 前台日期到后台的日期格式转换
    分享知识-快乐自己:SSH 整合 Demo
    分享知识-快乐自己:Struts2 拦截器 与 过滤器
    是否可以重定向到 WEB-INFO 下的页面?
    分享知识-快乐自己:Caused by: org.hibernate.tool.schema.extract.spi.SchemaExtractionException: More than one table found in namespace (, ) : Dept (XXX)
    分享知识-快乐自己:Struts2 (常用的 constant 总结)
    分享知识-快乐自己:Maven 无法加载 Oracle 数据库驱动源
    Java的不同版本
    用VIM写作
  • 原文地址:https://www.cnblogs.com/dand/p/10670769.html
Copyright © 2011-2022 走看看