gpt4 book ai didi

java - Java 中的单例服务和多线程

转载 作者:行者123 更新时间:2023-11-30 02:16:07 26 4
gpt4 key购买 nike

我正在使用 JAX-RS API 开发 Java 服务。我决定遵循单例模式,但现在我对如何管理并发有一些疑问。

以下是我的代码的简化示例:

@Singleton 
@Path("/")
public class NfvDeployer {

private static Map<String, List<String>> allocatedNodesOnHost;
private static Map<String, String> loadedHosts;
private static Map<String, String> loadedNodes;

...

@POST
@Path("nffgs/{id}/nodes")
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
public MyNode postNodeOnNFFG(MyNode Node, @PathParam("id") String id) {
...
synchronized (this) {
...
allocatedNodesOnHost.get(H).add(Node.ID);
}
}

@GET
@Path("hosts/{id}/nodes")
@Produces(MediaType.APPLICATION_XML)
public MyNodes getNodeFromNFFG(@PathParam("id") String id) {
...
for(String S : allocatedNodesOnHost.get(id)) {
...
}
}
}

您认为这种方法可行吗?所有 GET 请求应同时发生,而 POST 请求应串行化。正确吗?

最佳答案

有些事情可能无法按预期工作。首先,由于您将 NfvDeployer 声明为 @Singleton,因此您不必严格将所有字段设为静态。由于框架将确保仅存在其中一个,因此应该覆盖它。

关于同步,按原样存在问题。考虑当您写入作为 allocatedNodesOnHost 中的值包含的列表并同时迭代同一列表时的情况。您最终将得到一个ConcurrentModificationException。来自 Javadoc :

For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.

对此至少有两种解决方案,它们取决于您想要哪种保证。

选项 1:一致的数据 View

假设列表的读者和列表的作者必须对列表有一致的看法。含义:如果有人正在编写新值,则读者无法查看列表中包含的值的稍旧版本。为了解决这个问题,我们应该同步对 map (及其列表值)的所有访问:

public MyNode postNodeOnNFFG(MyNode node, @PathParam("id") String id) { 
synchronized (allocatedNodesOnHost) {
allocatedNodesOnHost.get(id).add(node.ID);
}
}

public MyNodes getNodeFromNFFG(@PathParam("id") String id) {
synchronized (allocatedNodesOnHost) {
for(String S : allocatedNodesOnHost.get(id)) {
...
}
}
}

您会注意到,我在 allocatedNodesOnHost 上同步,而不是在 this 上同步。通过在 this 上进行同步,我们获得了一个非常粗粒度的锁,它会影响我们可能设置的任何其他同步块(synchronized block)。再次强调,为了避免 ConcurrentModificationExceptions,所有对 allocatedNodesOnHost 的访问都需要同步。

选项 2:数据 View 不一致

当我说“不一致”时,我并不是说错误,我的意思是它可能稍微过时了,而且仅在并发修改的情况下。本质上,我们将在选项 1 中执行的同步卸载到 JVM 提供的结构中,该结构将为我们完成此操作。

首先,我们将使用 ConcurrentHashMap 声明我们的字段。 :

private Map<String, List<String>> allocatedNodesOnHost = new ConcurrentHashMap<>();

ConcurrentHashMap 为我们提供了一个映射,允许我们读取和写入映射,而无需同步它。它尽可能避免阻塞,因此当您知道将有并发的读取器和写入器时,最好在多线程应用程序中使用它。

当我们在该映射中创建一个值时,我们将使用 CopyOnWriteArrayList ,描述为:

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads.

请注意警告:如果您无法/不会同步并且没有频繁的写入者,请使用此选项。如果您不经常写入列表,这是一个可行的选择。

因此,当我们向 allocatedNodesOnHost 映射添加新列表时,我们会这样做:

allocatedNodesOnHost.put(hostName, new CopyOnWriteArrayList<>());

然后我们可以在访问allocatedNodesOnHost时放弃同步:

public MyNode postNodeOnNFFG(MyNode node, @PathParam("id") String id) { 
allocatedNodesOnHost.get(id).add(node.ID);
}

public MyNodes getNodeFromNFFG(@PathParam("id") String id) {
for(String S : allocatedNodesOnHost.get(id)) {
...
}
}

关于java - Java 中的单例服务和多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48356904/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com