课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > 并发性能优化
  • 并发性能优化

    发布:昆明Java培训      来源:Adrianos Dadis      时间:2016-08-17

  • 昆明java培训班的老师知道在高负载多线程应用中性能是非常重要的。为了达到更好的性能,开发者必须意识到并发的重要性。当我们需要使用并发时, 常常有一个资源必须被两个或多个线程共享。

    在这种情况下,就存在一个竞争条件,也就是其中一个线程可以得到锁(锁与特定资源绑定),其他想要得到锁的线程会被阻塞。这个同步机制的实现是有代价的,为了向你提供一个好用的同步模型,JVM和操作系统都要消耗资源。昆明java培训 班的老师知道有三个最重要的因素使并发的实现会消耗大量资源,它们是:

    上下文切换

    内存同步

    阻塞

    为了写出针对同步的优化代码,你必须认识到这三个因素以及如何减少它们。在写这样的代码时你需要注意很多东西。在本文中,我会向你介绍一种通过降低锁粒度的技术来减少这些因素。

    让我们从一个基本原则开始:不要长时间持有不必要的锁。

    在获得锁之前做完所有需要做的事,只把锁用在需要同步的资源上,用完之后立即释放它。我们来看一个简单的例子:

    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);

    }

    }

    上面的实现,通过基本的Grocery 锁来保护fruits和vegetables,因为同步是在方法域完成的。事实上,我们可以不使用这个大范围的锁,而是针对每个资源(fruits和vegetables)分别使用一个锁。来看一下改进后的代码:

    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);

    }

    }

    在使用了两个锁后(把锁分离),我们会发现比起之前用一个整体锁,锁阻塞的情况更少了。当我们把这个技术用在有中度锁争抢的锁上时,优化提升会更明显。如果把该方法应用到轻微锁争抢的锁上,改进虽然比较小,但还是有 效果的。但是如果把它用在有重度锁争抢的锁上时,你必须认识到结果并非总是更好。

    请有选择性的使用这个技术。如果你怀疑一个锁是重度争抢锁请按下面的方法来确认是否使用上面的技术:

    确认你的产品会有多少争抢度,将这个争抢度乘以三倍或五倍(甚至10倍,如果你想准备的万无一失)

    基于这个争抢度做适当的测试

    比较两种方案的测试结果,然后挑选出最合适的

    用于改进同步性能的技术还有很多,但对所有的技术来说最基本的原则只有一个:不要长时间持有不必要的锁。

    这条基本原则可以如我之前向你们解释的那样理解成“尽可能少的请求锁”,也可以有其他解释(实现方法)。

    并发性能优化的降低锁粒度昆明java培训班的老师已经讲完啦,同学们是不是也听懂了呢,8月31日昆明java培训班马上就要开班啦,大家快来抢好位置吧。

    推荐文章

上一篇:java编程实战线程基本概念

下一篇:ThreadLocal的内存泄漏问题

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56