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倍,这个决定于循环的次数

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

     

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

  • 相关阅读:
    LeetCode 623. Add One Row to Tree
    LeetCode 894. All Possible Full Binary Trees
    LeetCode 988. Smallest String Starting From Leaf
    LeetCode 979. Distribute Coins in Binary Tree
    LeetCode 814. Binary Tree Pruning
    LeetCode 951. Flip Equivalent Binary Trees
    LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
    LeetCode 889. Construct Binary Tree from Preorder and Postorder Traversal
    LeetCode 687. Longest Univalue Path
    LeetCode 428. Serialize and Deserialize N-ary Tree
  • 原文地址:https://www.cnblogs.com/cutter-point/p/6017631.html
Copyright © 2011-2022 走看看