java – 在线程之间共享数据库

前端之家收集整理的这篇文章主要介绍了java – 在线程之间共享数据库前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个应用程序是多线程和工作正常.然而,它遇到了锁争用问题(通过快照查看 java堆栈并查看等待的内容).

每个线程都从列表中消除对象,并拒绝每个线程或将其放入Bin.

箱子最初是空的,因为每个都可以是昂贵的(并且有可能很多它们).

导致争用的代码大致如下:

public void addToBin(Bin[] bins,Item item) {
   Bin bin;
   int bin_index = item.bin_index
   synchronized(bins) {
      bin = bins[bin_index];
      if(bin==null) {
        bin = new Bin();
        bins[bin_index] = bin;
      }
   }
   synchronized(bin) {
     bin.add(item);
   }
}

它是在瓶颈阵列上的同步,这是瓶颈.

有一位同事向我建议使用双重锁定来解决这个问题,但是我们不确定它会涉及什么来使其安全.建议的解决方案如下所示:

public void addToBin(Bin[] bins,Item item) {
   int bin_index = item.bin_index
   Bin bin = bins[bin_index];

   if(bin==null) {
     synchronized(bins) {
        bin = bins[bin_index];
        if(bin==null) {
          bin = new Bin();
          bins[bin_index] = bin;
        }
     }
   }

   synchronized(bin) {
     bin.add(item);
   }
}

这是安全的还是/或者有更好/更安全/更惯用的方式来做到这一点吗?

解决方法

如Malt的答案所述,Java已经提供了许多无锁数据结构和概念,可以用来解决这个问题.我想使用AtomicReferenceArray添加更详细的示例:

假设bins是一个AtomicReferenceArray,以下代码执行一个空闲的空闲更新,以防空条目:

Bin bin = bins.get(index);
while (bin == null) {
    bin = new Bin();
    if (!bins.compareAndSet(index,null,bin)) {
        // some other thread already set the bin in the meantime
        bin = bins.get(index);
    }
}
// use bin as usual

自从Java 8以来,有一个更优雅的解决方案:

Bin bin = bins.updateAndGet(index,oldBin -> oldBin == null ? new Bin() : oldBin);
// use bin as usual

节点:由于事实上,即使值不改变,updateAndGet将始终更新数组,Java 8版本仍然是非阻塞状态.这可能或可能不可忽略,取决于整个bin更新操作的总体成本.

另一个非常优雅的策略可能是将新建的Bin实例的整个bin数组预先填满,然后将数组移交给工作线程.由于线程不必修改阵列,这将减少与Bin对象本身同步的需要.通过使用Arrays.parallelSetAll(从Java 8开始)可以轻松完成数组填充多线程:

Arrays.parallelSetAll(bins,i -> new Bin());

更新2:如果这是一个选项取决于您的算法的预期输出:最终将数组填充完整,密集或只是稀疏? (在第一种情况下,预先填充是可取的,在第二种情况下,它依赖于这种情况,在后一种情况下,这可能是一个坏主意).

更新1:不要使用双重检查锁定!这不安全!这里的问题是可见性,而不是atomicitiy.在您的情况下,读取线程可能会部分构造(因此损坏)Bin实例.详见http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html.

猜你在找的Java相关文章