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

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

  • 相关阅读:
    js 点击列表li,获得当前li的id
    PHP松散比较与严格比较的区别详解
    电赛总结(二)——AD芯片总结之AD7705
    C++Premer Plus学习(五)——函数探幽
    FPGA学习
    AD7715
    电赛初探(二)——语音采集回放系统
    MATLAB信号与系统分析(五)——连续时间信号的频谱分析
    MATLAB信号与系统分析(四)——离散信号与系统的复频域分析及MATLAB实现
    MATLAB信号与系统分析(三)——连续信号与系统的复频域分析及MATLAB实现
  • 原文地址:https://www.cnblogs.com/dand/p/10670769.html
Copyright © 2011-2022 走看看