当前位置: 首页 > 面试题库 >

Java中的并发字节数组访问,锁尽可能少

魏学智
2023-03-14
问题内容

我正在尝试减少分段数据的锁定对象的内存使用量。这里看到我的问题。或者只是假设您有一个字节数组,并且每16个字节可以(反)序列化为对象。让我们将其称为行长度为16个字节的“行”。现在,如果您从写程序线程中修改这样的行并从多个线程中读取,则需要锁定。如果字节数组大小为1MB(1024 * 1024),则意味着65536行和相同数量的锁。

这有点太多,而且我需要更大的字节数组,并且我想将其减少到与线程数大致成比例的程度。我的想法是创建一个

ConcurrentHashMap<Integer, LockHelper> concurrentMap;

Integer行索引在哪里,在线程“进入”行之前,它在此映射中放置了一个锁定对象(从此答案中得到了这个想法)。但是,无论我怎么想,我都找不到真正线程安全的方法:

// somewhere else where we need to write or read the row
LockHelper lock1 = new LockHelper();
LockHelper lock = concurrentMap.putIfAbsent(rowIndex, lock1);
lock.addWaitingThread(); // is too late
synchronized(lock) {
  try { 
      // read or write row at rowIndex e.g. writing like
      bytes[rowIndex/16] = 1;
      bytes[rowIndex/16 + 1] = 2;
      // ...
  } finally {
     if(lock.noThreadsWaiting())
        concurrentMap.remove(rowIndex);
  }
}

您看到使该线程安全的可能性吗?

我感觉这看起来像concurrentMap.computecontstruct(例如,见此答案)非常相似,还是可以使用这种方法?

map.compute(rowIndex, (key, value) -> {
    if(value == null)
       value = new Object();
    synchronized (value) {
        // access row
        return value;
    }
});
map.remove(rowIndex);

正如我们已经知道计算操作是原子的那样,该值和“已同步”是否根本必要?

// null is forbidden so use the key also as the value to avoid creating additional objects
ConcurrentHashMap<Integer, Integer> map = ...;

// now the row access looks really simple:
map.compute(rowIndex, (key, value) -> {
    // access row
    return key;
});
map.remove(rowIndex);

顺便说一句:自从我们有了Java中的此计算之后。从1.8开始 在JavaDocs中找不到此

更新:
我在这里找到了一个非常相似的问题,使用的是userIds而不是rowIndices,请注意,该问题包含一个示例,其中包含一些问题,例如missing
finallocktry-finally-clause
内调用以及缺少缩小地图的问题。也似乎有一个用于此目的的JKeyLockManager库,但我认为它不是线程安全的。

更新2: Nicolas Filotto指出了如何避免删除该解决方案的方法似乎非常简单:

map.compute(rowIndex, (key, value) -> {
    // access row
    return null;
});

因此,这确实是减少了内存密集型的事情,但是synchronized在我的场景中,简单的段锁定至少快了50%。


问题答案:

synchronized正如我们已经知道计算操作是原子操作那样,该值和必要值是否全部存在?

我确认它不需要添加synchronized作为在这种情况下块compute方法是自动完成的,如对Javadoc表示ConcurrentHashMap#compute(Kkey, BiFunction<? super K,? super V,? extends V>remappingFunction)已经添加了BiFunction,因为Java 8,我引述如下:

尝试计算指定键及其当前映射值的映射(或者null如果没有当前映射)。 整个方法调用是原子执行的
。在计算进行过程中,可能会阻止其他线程对此映射进行的某些尝试的更新操作,因此计算应简短而简单,并且不得尝试更新此映射的任何其他映射Map

compute如果您BiFunction总是总是null以原子方式删除密钥,那么所有尝试都将以原子方式完成,那么您尝试使用该方法实现的目标可能完全是原子的。

map.compute(
    rowIndex, 
    (key, value) -> {
        // access row here
        return null;
    }
);

这样,您将完全依靠a的锁定机制ConcurrentHashMap来同步对行的访问。



 类似资料:
  • 我试图从发送到套接字的输入流中检索字节值。我用了很多方法,但它总是给我打印字节数组的地址,而不是它的内容。下面是我的客户端和服务器代码。当一个包从客户端发送到服务器时,服务器实例化一个新的线程来处理连接。所以slaveSocket是我想要使用的套接字。 } } }

  • 我正在尝试通过ObjectOutputStream将文件从服务器发送到客户端。我认为使用这个流发送不同的对象和原始文件数据是一种更简单的方法。

  • 上面的代码在我的主方法中。被注释掉的行抛出错误“无法在类车辆中找到符号”。我正在从一个文件中读取并将信息放置到正确的对象数据字段中。数组是车辆数组。根据我的理解,车辆数组的一个元素可以是车辆或车辆的任何子类。Getters和setters方法可用于每个相应的类和子类,使用父类的Getters和setters。汽车是车辆的一个子类。当我刚刚创建car对象时,为什么不在尝试vehicle之前先尝试访问

  • 主要内容:示例,死锁解决方案示例死锁描述了两个或多个线程等待彼此而被永久阻塞的情况。 当多个线程需要相同的锁定但以不同的顺序获取时,会发生死锁。 Java多线程程序可能会遇到死锁状况,因为关键字会导致执行线程在等待与指定对象相关联的锁定或监视时出现阻止情况。 看看下面一个例子。 示例 当您编译并执行上述程序时,会出现死锁情况,以下是程序生成的输出 - 上述程序将永久挂起,因为两个线程都不能继续进行,等待彼此释放锁定,所以您可以按

  • 问题内容: 我在想办法简单地分析字符串输入并在多维数组中找到正确的位置时遇到了麻烦。 我希望有一到两行能够做到这一点,因为我看到的解决方案依赖于较长的(10-20行)循环。 考虑下面的代码(注意,嵌套 可能 ,理论上是任意深度的): 是否可以简单地使用内置函数或其他易于重新创建所需输出的函数? 问题答案: 考虑为您的变量,你想获得或从(演示): 这是乐星串入 密钥 令牌,然后遍历在阵列 带键 的值

  • 本文向大家介绍Java并发问题之乐观锁与悲观锁,包括了Java并发问题之乐观锁与悲观锁的使用技巧和注意事项,需要的朋友参考一下 首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再