在另一个线程读取Java列表引用时更改该引用
我找不到这个问题的明确答案,所以如果其他地方回答得很好,我深表歉意 假设我有一个大致如下的东西:在另一个线程读取Java列表引用时更改该引用,java,multithreading,Java,Multithreading,我找不到这个问题的明确答案,所以如果其他地方回答得很好,我深表歉意 假设我有一个大致如下的东西: Class-SomeClass { 列表字符串=新的LinkedList(); public void updateList(列出新闻字符串){ 字符串=新闻字符串; } 公共列表GetMatchingString(字符串模式){ 列表匹配项=新建LinkedList(); 用于(字符串s:字符串){ 如果(matchesPattern(s,pattern)){//假设这一切正常 匹配项。添加(s)
Class-SomeClass
{
列表字符串=新的LinkedList();
public void updateList(列出新闻字符串){
字符串=新闻字符串;
}
公共列表GetMatchingString(字符串模式){
列表匹配项=新建LinkedList();
用于(字符串s:字符串){
如果(matchesPattern(s,pattern)){//假设这一切正常
匹配项。添加(s);
}
}
返回比赛;
}
}
假设另一个线程在调用getMatchingString()
时调用了updateList(…)
,是否会出现“问题”
预期的行为是,当前执行getMatchingString()
的线程将在旧列表上运行,随后的调用将在更新的列表上运行
感谢您的帮助 如果您的意思是会出现运行时异常,那么答案是否。迭代字符串的代码将使用迭代器调用时引用的任何内容 迭代时字符串引用是否更改并不重要。这样想,
Iterator<String> i = strings.iterator();
strings = new List<String>();
while (i.hasNext()) {
...
i.next();
}
Iterator i=strings.Iterator();
字符串=新列表();
while(i.hasNext()){
...
i、 next();
}
这个代码很好。字符串指向的对象未更改。我们只是将字符串引用改为指向其他地方。迭代器仍然指向赋值前字符串引用的原始对象
编辑:下面的一些评论说原始示例是不安全的出版物。我不相信是这样。不安全发布是指允许线程B访问线程A中正在构造的对象-线程A将对象不安全地发布到线程B
但是,在上面的示例中,没有关于所讨论的字段的对象构造。据我们所知,这两个列表都是以线程安全的方式完全构建的。只有引用赋值。Java中的对象赋值是Java语言规范中的原子赋值
“当线程使用变量的值时,
它获得的值实际上是存储在变量中的值
这是真的,即使程序
不包含正确同步的代码。例如,如果
线程将对不同对象的引用存储到同一引用中
值时,变量随后将包含对一个变量的引用
对象或其他对象,而不是对其他对象或对象的引用
已损坏的引用值。(长时间和长时间存在特殊异常。)
双值;见§17.4。)
现在,如果对象传递给
updateList(),则OP的代码中可能存在不安全的发布
不是安全构建的,但我不会假设我看不到的代码中可能存在哪些bug。Jeffrey是正确的-这里没有不安全的发布,因为任何时候都不会有一个线程构造对象,而另一个线程可以读取它(实例受updateList
方法调用的保护)。但是,访问不一致的可能性很大-例如,即使您首先调用updateList
,您最终迭代的列表也可能不是您期望的列表,因为没有显式的内存障碍。在这种情况下,JVM在运行时可能会做一些奇怪的事情
假设最终被迭代的列表(同样,这可能不是您期望的列表)不是空的,在迭代发生时没有修改,正确实现等等,那么这个“模式”将不会导致异常。也就是说,不应该使用它-您需要执行同步和(可能)如果要在多线程场景中使用,请将局部变量标记为
volatile
。代码中存在“不安全发布”(写入列表字段和读取列表字段之间没有“顺序前发生”)。使用阻止的“已同步”或非阻止的“volatile”或“AtomicReference”:
// with 'synchronized'
class SomeClass0 {
List<String> strings = new LinkedList<>();
public synchronized void updateList(List<String> newStrings) {
this.strings = newStrings;
}
public synchronized List<String> getMatchingStrings(String pattern) {
List<String> matches = new LinkedList<>();
for (String s : strings) {
if (matchesPattern(s, pattern)) { //Assume this just works
matches.add(s);
}
}
return matches;
}
}
// with 'volatile'
class SomeClass1 {
volatile List<String> strings = new LinkedList<>();
public void updateList(List<String> newStrings) {
this.strings = newStrings;
}
public List<String> getMatchingStrings(String pattern) {
List<String> localCopy = this.strings;
List<String> matches = new LinkedList<>();
for (String s : localCopy) {
if (matchesPattern(s, pattern)) { //Assume this just works
matches.add(s);
}
}
return matches;
}
}
// with 'AtomicReference'
class SomeClass2 {
AtomicReference<List<String>> strings =
new AtomicReference<>(new LinkedList<>());
public void updateList(List<String> newStrings) {
this.strings.set(newStrings);
}
public List<String> getMatchingStrings(String pattern) {
List<String> localCopy = this.strings.get();
List<String> matches = new LinkedList<>();
for (String s : localCopy) {
if (matchesPattern(s, pattern)) { //Assume this just works
matches.add(s);
}
}
return matches;
}
}
//带有“synchronized”
类0{
列表字符串=新的LinkedList();
公共同步的void updateList(列出新闻字符串){
this.strings=newStrings;
}
公共同步列表GetMatchingString(字符串模式){
列表匹配项=新建LinkedList();
用于(字符串s:字符串){
如果(matchesPattern(s,pattern)){//假设这一切正常
匹配项。添加(s);
}
}
返回比赛;
}
}
//使用“volatile”
一类{
volatile List strings=new LinkedList();
public void updateList(列出新闻字符串){
this.strings=newStrings;
}
公共列表GetMatchingString(字符串模式){
List localCopy=this.strings;
列表匹配项=新建LinkedList();
for(字符串s:localCopy){
如果(matchesPattern(s,pattern)){//假设这一切正常
匹配项。添加(s);
}
}
返回比赛;
}
}
//使用“原子引用”
第二类{
原子引用字符串=
新的原子引用(新的LinkedList());
public void updateList(列出新闻字符串){
this.strings.set(新闻字符串);
}
公共列表GetMatchingString(字符串模式){
List localCopy=this.strings.get();
列表匹配项=新建LinkedList();
for(字符串s:localCopy){
如果(matchesPattern(s,pattern)){//假设这一切正常
匹配项。添加(s);
}
}
返回比赛;
}
}
是否会出现“问题”?经验表明,在代码投入生产之前不会出现问题。但是,代码可能会选择新的引用,但没有内存障碍(以前发生过),它可能看不到完整的新列表,因为该列表可能仍在另一个CPU的CPU缓存中。因此,该列表可能已损坏并导致奇怪的结果,包括异常。@Andreas我的假设是