Reputation: 5147
I've a simple class which perform some check agains list of Regex patterns. My patterns are set somehow with constant list of values. This is not a question as for now, so I'm omitting that setting code.
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
}
Stuff is failry simple.
Now I'm accessing that class from 500 simultaenous threads. Class is created with default Spring scope singleton
.
Making access to patterns
synchronous:
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
synchronized(patterns){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
}
}
Well, since all threads access single instance of my class - they gets locked within synchronized block. Since my class is fairly lightweight I'd like to create separate instance for each thread (e.g. change scope from singleton
to prototype
). Removing synchronization block and adding getInstance
factory method backed by ThreadLocal
. Like that:
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
private static final ThreadLocal<Checker> checkerLocal =
new ThreadLocal<Checker>() {
@Override
protected Checker initialValue() {
System.out.println("Creating Checker instance")
return new Checker();
}
};
public static Checker getInstance(){
return checkerLocal.get();
}
}
Works like a charm.
However, imagine that I've to dynamically modify patterns somehow inside my application (e.g. add or remove Pattern there). I should perform that for all existing instances of Checker
class.
I'm thinking on using ApplicationContext#getBeansOfType(Checker.class)
method to get all bean instances. But is there any other more springy
way to do so?
Upvotes: 0
Views: 1153
Reputation: 77187
Here's my suggestion. The Checker
class doesn't have to have any synchronization at all, since it only ever pulls an unmodifiable List<Pattern>
out of the holder class, and the only synchronization needed is to prevent clashing updates on the holder.
public class PatternService {
AtomicReference<List<Pattern>> patterns = new AtomicReference<>(Collections.emptyList());
public List<Pattern> getPatterns() {
return patterns.get();
}
public synchronized void addPattern(Pattern p) {
List<Pattern> newPatterns = new LinkedList<>(patterns.get());
newPatterns.add(p);
patterns.set(Collections.unmodifiableList(newPatterns)); // or ImmutableList
}
// other mutators
}
public class Checker {
@Autowired PatternService patternService;
public boolean check(String param) {
for(Pattern p: patternService.getPatterns())
if(p.matcher(param).matches())
return true;
return false;
}
}
Upvotes: 2