Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing a Java List Reference While Another Thread Reads It

I couldn't find a clear answer to this question, so apologies if this is nicely answered elsewhere.

Imagine I have something roughly like:

Class SomeClass
{
  List<String> strings = new LinkedList<>();


  public void updateList(List<String> newStrings) {
    strings = newStrings;
  }

  public 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;
  }

}

Assuming another thread calls updateList(...) while some thread is calling getMatchingStrings(), will there be a "problem"?

The intended behavior is that the thread currently executing getMatchingStrings() will run on the old list, and a subsequent call will run on the updated list.

Any help is appreciated!

like image 475
Mike Avatar asked Feb 09 '26 21:02

Mike


1 Answers

There is 'unsafe publication' in you code (no 'happens-before order' between write list field and read list field). Use blocked 'synchronized' or non-blocked 'volatile' or '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;
    }
}
like image 153
Ivan Golovach Avatar answered Feb 12 '26 11:02

Ivan Golovach



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!