zoukankan      html  css  js  c++  java
  • java并发编程(4)性能与可伸缩性

    性能与可伸缩性

    一、Amdahl定律

    1.问题和资源的关系

        在某些问题中,资源越多解决速度越快;而有些问题则相反:

      注意:每个程序中必然有串行的部分,而合理的分析出串行和并行的部分对程序的影响极大;串行部分占比和多核执行效率之间是指数级别的关系

    2.ConcurrentLinkedQueue

      在多核环境中,这个线程安全的队列比通过synchronizedList生成的队列速度要快很多

      可以说:concurrent中提供的类,比通过方法生成的线程安全类速度要快

    二、线程开销

      由于多线程有开销:所以使用多线程必须保证性能的提升>并发的开销

      上下文切换的开销

      内存同步的开销

    三、减少锁的竞争

      1.减少锁持有时间:缩小锁的范围

    private final Map<String, String> attributes = new HashMap<String, String>();
    
    //整个方法上锁
        public synchronized boolean userLocationMatches(String name, String regexp) {
            String key = "users." + name + ".location";
            String location = attributes.get(key);
            if (location == null)
                return false;
            else
                return Pattern.matches(regexp, location);
        }
    
    public boolean userLocationMatches(String name, String regexp) {
            String key = "users." + name + ".location";
            String location;
            //只针对可变状态上锁
            synchronized (this) {
                location = attributes.get(key);
            }
            if (location == null)
                return false;
            else
                return Pattern.matches(regexp, location);
        }

      2.降低锁的请求频率:锁分解、锁分段...

        锁分解:将一个锁分解为多个锁如:无需在一个原子操作中更新多个状态变量,每个状态变量却用的是同一个类锁,就没必要,每个不相干的状态变量的使用自己的锁就行

    public class ServerStatusBeforeSplit {
        public final Set<String> users;
        public final Set<String> queries;
    
        public ServerStatusBeforeSplit() {
            users = new HashSet<String>();
            queries = new HashSet<String>();
        }
        //每个方法使用 当前class实例锁,类似于synchronized(this),不管是否是操作同一共享状态
        public synchronized void addUser(String u) {
            users.add(u);
        }
    
        public synchronized void addQuery(String q) {
            queries.add(q);
        }
    
        public synchronized void removeUser(String u) {
            users.remove(u);
        }
    
        public synchronized void removeQuery(String q) {
            queries.remove(q);
        }
    }
    
    public class ServerStatusAfterSplit {
        public final Set<String> users;
        public final Set<String> queries;
        //操作同一 状态的方法 使用相同的锁
        public ServerStatusAfterSplit() {
            users = new HashSet<String>();
            queries = new HashSet<String>();
        }
        public void addUser(String u) {
            synchronized (users) {
                users.add(u);
            }
        }
        public void addQuery(String q) {
            synchronized (queries) {
                queries.add(q);
            }
        }
        public void removeUser(String u) {
            synchronized (users) {
                users.remove(u);
            }
        }
        public void removeQuery(String q) {
            synchronized (users) {
                queries.remove(q);
            }
        }
    }

      

      锁分段:如将map桶分成不同的段,每个段都有一个锁,这样,在执行某些操作如get,就可以持有不同的锁从而提高并发效率,当然有些操作需要同时持有容器所有段的锁如clear等

    //Map分段锁实现
    public class StripedMap {
        // Synchronization policy: buckets[n] guarded by locks[n%N_LOCKS]
        private static final int N_LOCKS = 16;  //锁数量
        private final Node[] buckets;           //容器桶
        private final Object[] locks;           //同步监听器对象数组
        private static class Node {
            Node next;
            Object key;
            Object value;
        }
    
        public StripedMap(int numBuckets) {
            buckets = new Node[numBuckets];
            locks = new Object[N_LOCKS];
            for (int i = 0; i < N_LOCKS; i++)
                locks[i] = new Object();
        }
        private final int hash(Object key) {
            return Math.abs(key.hashCode() % buckets.length);
        }
        public Object get(Object key) {
            int hash = hash(key);
            //获取当前 key对应的index区域的锁,只获取了一个锁
            synchronized (locks[hash % N_LOCKS]) {
                for (Node m = buckets[hash]; m != null; m = m.next)
                    if (m.key.equals(key))
                        return m.value;
            }
            return null;
        }
        public void clear() {
            for (int i = 0; i < buckets.length; i++) {
                //获取 每个i对应的锁,就是获取了整个容器所有的分段锁
                synchronized (locks[i % N_LOCKS]) {
                    buckets[i] = null;
                }
            }
        }
    }

      3.避免热点域

        热点资源的锁竞争激烈,导致的性能问题

      4.替代独占锁

        如:读-写锁:读读可并行,来防止独占;使用原子状态量;使用并发容器;使用不可变对象等

      5.减少上下文切换

        任务在阻塞于非阻塞的状态中切换,就类似于一次上下文切换

        如:日志,日志的打印和IO操作会导致大量的阻塞和释放,导致性能问题

  • 相关阅读:
    JavaScript数组迭代方法
    Ant Design Mobile RN中Toast不起作用的原因【坑篇】
    解决vsCode终端不能运行yarn脚本
    k8s——Service和Ingress
    Prometheus学习
    k8s——pod控制器
    k8s——管理pod资源对象
    k8s——资源管理基础
    docker学习
    k8s学习——Helm入门及使用
  • 原文地址:https://www.cnblogs.com/zhangxinly/p/6938045.html
Copyright © 2011-2022 走看看