zoukankan      html  css  js  c++  java
  • 【JAVA并发编程实战】5、构建高效且可伸缩的结果缓存

    首先创建一个借口,用来表示耗费资源的计算

    package cn.xf.cp.ch05;
    
    public interface Computable<A, V>
    {
        V compute(A arg) throws Exception;
    }

    实现接口,实现计算过程

    package cn.xf.cp.ch05;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.math.BigInteger;
    
    public class ExpensiveFunction implements Computable<String, BigInteger>
    {
    
        @Override
        public BigInteger compute(String arg) throws InterruptedException
        {
            //读取10个文件的和
            BigInteger count = new BigInteger("0");
            File file = null;
            InputStreamReader is = null;
            BufferedReader br = null;
            try
            {
                file = new File(String.valueOf(arg));
                is = new InputStreamReader(new FileInputStream(file));
                br = new BufferedReader(is);
                String line = "";
                while((line = br.readLine()) != null)
                {
                    String longdata[] = line.split(" ");
                    for(int j = 0; j < longdata.length; ++j)
                    {
                        //统计和
                        String temp = longdata[j];
                        BigInteger bitemp = new BigInteger(temp);
                        count = count.add(bitemp);
                    }
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                try
                {
                    br.close();
                    is.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            //返回统计结果
            return count;
        }
        
        @org.junit.Test
        public void test()
        {
            String s = "132456789123456789326549873218746132165432176134653216874649761";
            BigInteger bi = new BigInteger("0");
            
            System.out.println(bi.add(new BigInteger("1")).toString());
        }
    }

    功能实现1,这个不是现场安全的,就是一个简单的缓存机制,如果有就直接从缓存中获取,没有的话就添加到缓存中

    package cn.xf.cp.ch05;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class Memoizer1<A, V> implements Computable<A, V>
    {
        /**
         * 这里使用hashmap来作为缓存对象,其实并不是一个好的选择,hashmap并不是一个线程安全的类
         */
        private final Map<A, V> cache = new HashMap<A, V>();
        private final Computable<A, V> c;
        
        public Memoizer1(Computable<A, V> c)
        {
            //初始化C的值
            this.c = c;
        }
        
        /**
         *    由于hashmap并不是一个线程安全的,所以我们队这个操作加锁,避免意外
         *    但是如果有一个问题,如果一个线程阻塞了这个方法,那么其他线程就无法通过这个方法获取到对应的缓存
         *    那么就可能会造成使用缓存的结果却比不使用缓存更慢,性能并不会得到任何的提升
         * @param arg
         * @return
         * @throws InterruptedException
         */
        @Override
        public synchronized V compute(A arg) throws Exception
        {
            V result = cache.get(arg);
            if(result == null)
            {
                result = c.compute(arg);
                //如果没有的话,那么就放到对应的缓存对象中
                cache.put(arg, result);
            }
            return result;
        }
    
    }

    功能实现2:添加一个future进行异步处理,就是把原来的数据计量分化到异步处理中,这样就不会产生阻塞在一个地方,造成其他线程等待很耗费资源的计算产生结果的问题

    package cn.xf.cp.ch05;
    
    import java.util.Map;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.Future;
    import java.util.concurrent.FutureTask;
    
    public class Memoizer2<A, V> implements Computable<A, V>
    {
        private final Map<A, Future<V>>    cache    = new ConcurrentHashMap<A, Future<V>>();
        private final Computable<A, V>    c;
    
        public Memoizer2(Computable<A, V> c) 
        {
            this.c = c;
        }
        
        /**
         * 这个里面的复合操作,没有就添加future,是在底层的MAP对象上执行的,而map无法通过加锁来确定原子性
         */
        @Override
        public V compute(final A arg) throws Exception
        {
            Future<V> f = cache.get(arg);
            if (f == null)
            {
                Callable<V> eval = new Callable<V>()
                {
                    public V call() throws Exception
                    {
                        return c.compute(arg);
                    }
                };
                FutureTask<V> ft = new FutureTask<V>(eval);
                f = ft;
                cache.put(arg, ft);
                ft.run(); // call to c.compute happens here
            }
            return f.get();
        }
        
    }

    功能实现3:避免底层map的操作有影响,并防止缓存污染

    package cn.xf.cp.ch05;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.Future;
    import java.util.concurrent.FutureTask;
    
    public class Memoizer3<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 Memoizer3(Computable<A, V> c)
        {
            this.c = c;
        }
        
        @Override
        public V compute(final A arg) throws Exception
        {
            //不断循环,直到return
            while (true)
            {
                Future<V> f = cache.get(arg);
                if (f == null)
                {
                    Callable<V> eval = new Callable<V>()
                    {
                        public V call() throws Exception
                        {
                            return c.compute(arg);
                        }
                    };
                    FutureTask<V> ft = new FutureTask<V>(eval);
                    /*
                     * 确定使用,这个避免重复添加
                     * 如果包含arg就返回,否则就把ft放入进去
                     */
                    f = cache.putIfAbsent(arg, ft);
                    if (f == null)
                    {
                        f = ft;
                        ft.run();
                    }
                }
                try
                {
                    //返回计算结果
                    return f.get();
                }
                catch (Exception e)
                {
                    //计算失败,从缓存中移除,避免缓存污染,就是一个key返回一个空值,并返回从新运算
                    cache.remove(arg, f);
                }
            }
        }
    
    }

    测试结果:

    package cn.xf.cp.ch05;
    
    import java.math.BigInteger;
    
    public class MemeryTest
    {
        public static void main(String[] args) throws Exception
        {
            ExpensiveFunction ef = new ExpensiveFunction();
            Memoizer1<String, BigInteger> m = new Memoizer1<String, BigInteger>(ef);
            BigInteger bresult1 = new BigInteger("0");
            BigInteger bresult2 = new BigInteger("0");
            //比较不同方式统计10次1文件的时间比较
            long start1 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult1 = bresult1.add(ef.compute(String.valueOf(1)));
            }
            long end1 = System.nanoTime();
            
            long start2 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult2 = bresult2.add(m.compute(String.valueOf(1)));
            }
            long end2 = System.nanoTime();
            
            System.out.println("统计文件1~10使用时间" + (end1 - start1) + "  大小是:" + bresult1.toString());
            
            System.out.println("统计文件1,10次使用时间" + (end2 - start2) + "  大小是:" + bresult2.toString());
        }
        
        @org.junit.Test
        public void test2() throws Exception
        {
            ExpensiveFunction ef = new ExpensiveFunction();
            Memoizer2<String, BigInteger> m = new Memoizer2<String, BigInteger>(ef);
            BigInteger bresult1 = new BigInteger("0");
            BigInteger bresult2 = new BigInteger("0");
            //比较不同方式统计10次1文件的时间比较
            long start1 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult1 = bresult1.add(ef.compute(String.valueOf(1)));
            }
            long end1 = System.nanoTime();
            
            long start2 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult2 = bresult2.add(m.compute(String.valueOf(1)));
            }
            long end2 = System.nanoTime();
            
            System.out.println("统计文件1~10使用时间" + (end1 - start1) + "  大小是:" + bresult1.toString());
            
            System.out.println("统计文件1,10次使用时间" + (end2 - start2) + "  大小是:" + bresult2.toString());
        
        }
        
        @org.junit.Test
        public void test3() throws Exception
        {
    
            ExpensiveFunction ef = new ExpensiveFunction();
            Memoizer3<String, BigInteger> m = new Memoizer3<String, BigInteger>(ef);
            BigInteger bresult1 = new BigInteger("0");
            BigInteger bresult2 = new BigInteger("0");
            //比较不同方式统计10次1文件的时间比较
            long start1 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult1 = bresult1.add(ef.compute(String.valueOf(1)));
            }
            long end1 = System.nanoTime();
            
            long start2 = System.nanoTime();
            for(int i = 1; i <= 10; ++i)
            {
                bresult2 = bresult2.add(m.compute(String.valueOf(1)));
            }
            long end2 = System.nanoTime();
            
            System.out.println("统计文件1~10使用时间" + (end1 - start1) + "  大小是:" + bresult1.toString());
            
            System.out.println("统计文件1,10次使用时间" + (end2 - start2) + "  大小是:" + bresult2.toString());
        
        
        }
    }

    结果就是使用了缓存的话,速度会快10倍,这个决定于循环的次数

    文件的话,就是简单的循环生成数据

     

    三种测试结果,差不多,因为这里并没有使用到多线程进行测试,结果和单线程是一样的。 

  • 相关阅读:
    sql对查询为null的值赋默认值
    org.apache.coyote.http11.Http11Processor.service 解析 HTTP 请求 header 错误
    linux使用telnet测试端口
    grep和sed拉取线上日志,nc导入elk
    linux命令sed使用
    windows使用nc命令
    离线日志数据导入elk
    docker搭建elk日志收集系统
    curl: (60) Peer's Certificate has expired
    【Splay】【启发式合并】hdu6133 Army Formations
  • 原文地址:https://www.cnblogs.com/cutter-point/p/6017631.html
Copyright © 2011-2022 走看看