繁体   English   中英

使用信号量保护数组

[英]Using semaphores to protect an array

假设有许多线程调用方法m(int i)并更改位置i处的数组的值。 以下代码正确,还是存在竞争条件?

public class A{
    private int []a =new int[N];
    private Semaphore[] s=new Semaphore[N];

    public A(){
        for(int i =0 ; i<N ; i++)
           s[i]=new Semaphore(1);
    }

    public void m(int i){
        s[i].acquire();
        a[i]++;
        s[i].release();
    }
}

代码是正确的,尽管as都应设为final ,但我看不到任何竞争条件。 每次使用需要获取和释放的锁时,也应该尝试/最后使用:

s[i].acquire();
try {
   a[i]++;
} finally {
   s[i].release();
}

但是,对于更新数组,每个项目单独锁的想法是非常不必要的。 单个锁将是适当的,因为主要的成本是内存更新和其他本机同步。 这就是说,如果实际操作不是 int ++则可以保证使用Semaphore或其他Lock对象。

但是对于简单的操作,可以使用如下所示的方法:

// make sure it is final if you are synchronizing on it
private final int[] a = new int[N];
...

public void m(int i) {
   synchronized (a) {
      a[i]++:
   }
}

如果您真的担心阻塞,那么另一种可能性就是使用AtomicInteger数组,但是除非探查器另行通知,否则即使这样也感觉有些过头了。

private final AtomicInteger[] a = new AtomicInteger[N];
...

public A(){
    for(int i = 0; i < N; i++)
       a[i] = new AtomicInteger(0);
}

public void m(int i) {
    a[i].incrementAndGet();
}

编辑:

我刚刚编写了一个快速的愚蠢的测试程序 ,该程序比较单个synchronized锁,在锁数组, AtomicInteger数组和Semaphore数组上synchronized的锁。 结果如下:

  • int[] 10617ms上synchronized
  • Object[]数组上synchronized 1827ms
  • AtomicInteger数组1414ms
  • Semaphore数组3211ms

但是 ,最重要的是,这有10个线程,每个线程执行1000万次迭代。 当然,它速度更快,但是除非您确实进行了数百万次迭代,否则您的应用程序不会看到任何明显的性能改进。 这是“过早优化”的定义。 将为代码复杂性,增加出现错误的可能性,增加调试时间,增加维护成本等付出代价。引用Knuth:

我们应该忘记效率低下的问题,例如大约97%的时间:过早的优化是万恶之源。

现在,正如OP在评论中所暗示的那样, i++并不是他/她正在保护的真正操作。 如果增量消耗大量时间(即,如果增加了阻塞),那么将需要锁阵列。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM