Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在另一个线程读取Java列表引用时更改该引用_Java_Multithreading - Fatal编程技术网

在另一个线程读取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我的假设是