- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这是交易。我有一个 HashMap ,其中包含我称为“程序代码”的数据,它存在于一个对象中,如下所示:
Class Metadata
{
private HashMap validProgramCodes;
public HashMap getValidProgramCodes() { return validProgramCodes; }
public void setValidProgramCodes(HashMap h) { validProgramCodes = h; }
}
我有很多很多读取器线程,每个线程都会调用一次 getValidProgramCodes(),然后将该 HashMap 用作只读资源。
到目前为止一切顺利。这就是我们变得有趣的地方。
我想放入一个计时器,它每隔一段时间生成一个新的有效程序代码列表(不管如何),并调用 setValidProgramCodes。
我的理论——我需要帮助来验证——是我可以继续按原样使用代码,而无需进行显式同步。它是这样的:在更新 validProgramCodes 时,validProgramCodes 的值总是好的——它是指向新 HashMap 或旧 HashMap 的指针。 这是一切都取决于的假设。拥有旧散列图的读者是可以的;他可以继续使用旧值,因为在他释放它之前它不会被垃圾回收。每个读者都是短暂的;它很快就会死去,并被一个新的取而代之并获得新的值(value)。
这站得住脚吗?我的主要目标是在绝大多数没有更新发生的情况下避免昂贵的同步和阻塞。我们大约每小时只更新一次,读者不断地进进出出。
最佳答案
这是一个线程关心另一个线程在做什么的情况吗?然后 JMM FAQ有答案:
Most of the time, one thread doesn't care what the other is doing. But when it does, that's what synchronization is for.
针对那些说 OP 的代码按原样是安全的人的回应,考虑一下:Java 的内存模型中没有任何东西可以保证在启动新线程时该字段将刷新到主内存。此外,只要更改在线程中检测不到,JVM 就可以自由地重新排序操作。
从理论上讲,读取器线程不能保证看到对 validProgramCodes 的“写入”。在实践中,他们最终会,但你不能确定什么时候。
我建议将 validProgramCodes 成员声明为“volatile”。速度差异可以忽略不计,并且无论引入何种 JVM 优化,它都将保证您的代码现在和将来的安全。
这里有一个具体的建议:
import java.util.Collections;
class Metadata {
private volatile Map validProgramCodes = Collections.emptyMap();
public Map getValidProgramCodes() {
return validProgramCodes;
}
public void setValidProgramCodes(Map h) {
if (h == null)
throw new NullPointerException("validProgramCodes == null");
validProgramCodes = Collections.unmodifiableMap(new HashMap(h));
}
}
除了用 unmodifiableMap
包装它之外,我还在复制映射 (new HashMap(h)
)。这使得即使 setter 的调用者继续更新映射“h”也不会改变的快照。例如,他们可能会清除 map 并添加新条目。
在风格上,通常最好用 List
和 Map
等抽象类型声明 API,而不是像 ArrayList
这样的具体类型和 HashMap。
如果具体类型需要更改(就像我在这里所做的那样),这会在将来提供灵 active 。
将“h”分配给“validProgramCodes”的结果可能只是对处理器缓存的写入。即使在新线程启动时,“h”对新线程也是不可见的,除非它已被刷新到共享内存。除非有必要,否则良好的运行时将避免刷新,使用 volatile
是表明这是必要的一种方式。
假设以下代码:
HashMap codes = new HashMap();
codes.putAll(source);
meta.setValidProgramCodes(codes);
如果 setValidCodes
只是 OP 的 validProgramCodes = h;
,编译器可以像这样自由地重新排序代码:
1: meta.validProgramCodes = codes = new HashMap();
2: codes.putAll(source);
假设在编写器第 1 行执行后,读取器线程开始运行此代码:
1: Map codes = meta.getValidProgramCodes();
2: Iterator i = codes.entrySet().iterator();
3: while (i.hasNext()) {
4: Map.Entry e = (Map.Entry) i.next();
5: // Do something with e.
6: }
现在假设writer线程在reader的第2行和第3行之间的map上调用了“putAll”,Iterator底层的map经历了一次并发修改,抛出了一个runtime exception——一个非常断断续续的,看似莫名其妙的runtime exception在测试期间从未产生过。
任何时候你有一个线程关心另一个线程在做什么,你必须有某种内存屏障来确保一个线程的操作对另一个线程可见。如果一个线程中的事件必须在另一个线程中的事件之前发生,您必须明确指出。否则没有任何保证。实际上,这意味着 volatile
或 synchronized
。
不要吝啬。错误程序无法完成工作的速度有多快并不重要。此处显示的示例简单而人为,但请放心,它们说明了现实世界中的并发错误,由于其不可预测性和平台敏感性,这些错误非常难以识别和解决。
关于Java并发场景——需要同步还是不需要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/300316/
我正在实现 IMAP 客户端,但 IMAP 邮箱同步出现问题。 首先,可以从 IMAP 服务器获取新邮件,但我不知道如何从邮箱中查找已删除的邮件。 我是否应该从服务器获取所有消息并将其与本地数据进行比
我研究线程同步。当我有这个例子时: class A { public synchronized void methodA(){ } public synchronized void met
嗨,我做了一个扩展线程的东西,它添加了一个包含 IP 的对象。然后我创建了该线程的两个实例并启动它们。他们使用相同的列表。 我现在想使用 Synchronized 来阻止并发更新问题。但它不起作用,我
我正在尝试使用 FTP 定期将小数据文件从程序上传到服务器。用户从使用 javascript XMLHttpRequest 函数读取数据的网页访问数据。这一切似乎都有效,但我正在努力解决由 FTP 和
我不知道如何同步下一个代码: javascript: (function() { var s2 = document.createElement('script'); s2.src =
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 7 年前。 Improve this qu
一 点睛 1 Message 在基于 Message 的系统中,每一个 Event 也可以被称为 Message,Message 是对 Event 更高一个层级的抽象,每一个 Message 都有一个
一 点睛 1 Message 在基于 Message 的系统中,每一个 Event 也可以被称为 Message,Message 是对 Event 更高一个层级的抽象,每一个 Message 都有一个
目标:我所追求的是每次在数据库中添加某些内容时(在 $.ajax 到 Submit_to_db.php 之后),从数据库获取数据并刷新 main.php(通过 draw_polygon 更明显)。 所
我有一个重复动画,需要与其他一些 transient 动画同步。重复动画是一条在屏幕上移动 4 秒的扫描线。当它经过下面的图像时,这些图像需要“闪烁”。 闪烁的图像可以根据用户的意愿来来去去和移动。它
我有 b 个块,每个块有 t 个线程。 我可以用 __syncthreads() 同步特定块中的线程。例如 __global__ void aFunction() { for(i=0;i #
我正在使用azure表查询来检索分配给用户的所有错误实体。 此外,我更改了实体的属性以声明该实体处于处理模式。 处理完实体后,我将从表中删除该实体。 当我进行并行测试时,可能会发生查询期间,一个实体已
我想知道 SQLite 是如何实现它的。它基于文件锁定吗?当然,并不是每个访问它的用户都锁定了整个数据库;那效率极低。它是基于多个文件还是仅基于一个大文件? 如果有人能够简要概述一下 sqlite 中
我想post到php,当id EmpAgree1时,然后它的post变量EmpAgree=1;当id为EmpAgree2时,则后置变量EmpAgree=2等。但只是读取i的最后一个值,为什么?以及如何
CUBLAS 文档提到我们在读取标量结果之前需要同步: “此外,少数返回标量结果的函数,例如 amax()、amin、asum()、rotg()、rotmg()、dot() 和 nrm2(),通过引用
我知道下面的代码中缺少一些内容,我的问题是关于 RemoteImplementation 中的同步机制。我还了解到该网站和其他网站上有几个关于 RMI 和同步的问题;我在这里寻找明确的确认/矛盾。 我
我不太确定如何解决这个问题......所以我可能需要几次尝试才能正确回答这个问题。我有一个用于缓存方法结果的注释。我的代码目前是一个私有(private)分支,但我正在处理的部分从这里开始: http
我对 Java 非常失望,因为它不允许以下代码尽可能地并发移动。当没有同步时,两个线程会更频繁地切换,但是当尝试访问同步方法时,在第二个线程获得锁之前以及在第一个线程获得锁之前再次花费太长时间(比如
过去几周我一直在研究java多线程。我了解了synchronized,并理解synchronized避免了多个线程同时访问相同的属性。我编写此代码是为了在同一线程中运行两个线程。 val gate =
我有一个关于 Java 同步的简单问题。 请假设以下代码: public class Test { private String address; private int age;
我是一名优秀的程序员,十分优秀!