并发优化–减少锁粒度

在高负载多线程应用程序中,性能非常重要。 开发人员必须意识到并发问题才能获得更好的性能。 当我们需要并发时,我们通常拥有必须由两个或更多线程共享的资源。

在这种情况下,我们有一个竞争条件 ,其中只有一个线程(在资源上)将获得锁,而所有其他需要该锁的线程将被阻塞。 此同步功能不是免费提供的。 JVM和OS都会消耗资源,以便为您提供有效的并发模型。 使并发实现资源密集的三个最基本的因素是:

  • 上下文切换
  • 内存同步
  • 封锁

    为了编写优化的代码进行同步,您必须了解这三个因素以及如何减少它们。 编写此类代码时,必须注意许多事项。 在本文中,我将向您展示一种通过减少锁的粒度来减少这些因素的技术。

    从基本规则开始: 不要将锁持有超过必要的时间。

    获取锁之前 ,请做任何您需要做的事情, 仅将锁用于对同步资源进行操作并立即释放它。 看一个简单的例子:

    public class HelloSync {
        private Map dictionary = new HashMap();
        public synchronized void borringDeveloper(String key, String value) {
            long startTime = (new java.util.Date()).getTime();
            value = value + "_"+startTime;
            dictionary.put(key, value);
            System.out.println("I did this in "+
         ((new java.util.Date()).getTime() - startTime)+" miliseconds");
        }
    }

    在此示例中,我们违反了基本规则,因为我们创建了两个Date对象,调用System.out.println()并执行许多String串联。 唯一需要同步的动作是动作:“ dictionary.put(key,value); ”更改代码并将同步从方法范围移到这一行。 更好的代码是这样的:

    public class HelloSync {
        private Map dictionary = new HashMap();
        public void borringDeveloper(String key, String value) {
            long startTime = (new java.util.Date()).getTime();
            value = value + "_"+startTime;
            synchronized (dictionary) {
                dictionary.put(key, value);
            }
            System.out.println("I did this in "+
     ((new java.util.Date()).getTime() - startTime)+" miliseconds");
        }
    }

    上面的代码可以写得更好,但是我只想给你个想法。 如果想知道如何做,请检查java.util.concurrent.ConcurrentHashMap

    那么,如何减少锁的粒度呢? 简而言之,通过尽可能少地请求锁。 基本思想是使用单独的锁来保护一个类的多个独立状态变量,而不是在类范围内仅具有一个锁。 看看我在许多应用程序中看到的这个简单示例。

    public class Grocery {
        private final ArrayList fruits = new ArrayList();
        private final ArrayList vegetables = new ArrayList();
        public synchronized void addFruit(int index, String fruit) {
            fruits.add(index, fruit);
        }
        public synchronized void removeFruit(int index) {
            fruits.remove(index);
        }
        public synchronized void addVegetable(int index, String vegetable) {
            vegetables.add(index, vegetable);
        }
        public synchronized void removeVegetable(int index) {
            vegetables.remove(index);
        }
    }

    杂货店老板可以在他的杂货店添加水果和蔬菜/从中删除水果和蔬菜。 杂货店的这种实现使用基本的杂货店锁来保护水果和蔬菜,因为同步是在方法范围内完成的。 除了使用这种胖锁,我们可以使用两个单独的守护程序,每种资源(水果和蔬菜)使用一个。 检查下面的改进代码。

    public class Grocery {
        private final ArrayList fruits = new ArrayList();
        private final ArrayList vegetables = new ArrayList();
        public void addFruit(int index, String fruit) {
            synchronized(fruits) fruits.add(index, fruit);
        }
        public void removeFruit(int index) {
            synchronized(fruits) {fruits.remove(index);}
        }
        public void addVegetable(int index, String vegetable) {
            synchronized(vegetables) vegetables.add(index, vegetable);
        }
        public void removeVegetable(int index) {
            synchronized(vegetables) vegetables.remove(index);
        }
    }

    使用两个防护装置(拆分锁)后,我们看到的锁定流量将少于原始的胖锁。 当我们将其应用于具有中等锁争用的锁时,此技术会更好地工作。 如果我们将其应用于具有轻微争用的锁,则收益很小,但仍为正值。 如果我们将其应用于争用较大的锁,则结果并不总是更好,您必须意识到这一点。

    请出于良心使用此技术。 如果您怀疑这是一个争用锁,请按照以下步骤操作:

    • 确认您的生产需求量,将其乘以3或5(即使您愿意准备,甚至乘以10)。
    • 根据新流量在您的测试平台上运行适当的测试。
    • 比较两个解决方案,然后才选择最合适的解决方案。

      有更多技术可以提高同步性能,但是对于所有技术,基本规则是: 保持锁的时间不要超过必要的时间

      正如我已经向您解释的那样,此基本规则可以翻译为“尽可能少地寻求锁”,也可以翻译为其他翻译(解决方案),我将在以后的文章中尝试描述它们。

      另外两个重要的建议:

      • 请注意java.util.concurrent包(和子包) 的类,因为它们有非常聪明和有用的实现。
      • 通过使用良好的设计模式,大多数时候并发代码可以最小化。 始终牢记企业集成模式 ,它们可以节省您的时间。

        参考: 减少锁粒度–我们Java的 JCG合作伙伴 Adrianos Dadis的并发优化 ,Integration and the sources 的优点

        相关文章 :

        翻译自: https://www.javacodegeeks.com/2011/10/concurrency-optimization-reduce-lock.html

        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

        当前余额3.43前往充值 >
        需支付:10.00
        成就一亿技术人!
        领取后你会自动成为博主和红包主的粉丝 规则
        hope_wisdom
        发出的红包
        实付
        使用余额支付
        点击重新获取
        扫码支付
        钱包余额 0

        抵扣说明:

        1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
        2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

        余额充值