I have a resource file that I load, when needed, to check if a certain word is among the ones in the file.
I want to get rid of the method-wide synchronized keyword. There’s no point in all threads waiting while wordsInFile.contains(word.toLowerCase());
runs.
Original:
private Set<String> wordsInFile = new HashSet<String>; private static synchronized boolean IsFound(String word) { if (word == null) { return false; } if (wordsInFile.isEmpty()) { wordsInFile = new HashSet<String>(); InputStream inputStream = WebUtils.class.getClassLoader().getResourceAsStream("words.txt"); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String line = bufferedReader.readLine(); while (line != null) { wordsInFile.add(line); line = bufferedReader.readLine(); // they're all lowercase } } catch (Exception aEx) { ; } } return wordsInFile.contains(word.toLowerCase()); }
Is this OK?
private Set<String> wordsInFile; private static boolean IsFound(String word) { if (word == null) { return false; } synchronized (wordsInFile) { if (wordsInFile.isEmpty()) { wordsInFile = new HashSet<String>(); InputStream inputStream = WebUtils.class.getClassLoader().getResourceAsStream("words.txt"); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String line = bufferedReader.readLine(); while (line != null) { wordsInFile.add(line); line = bufferedReader.readLine(); // they're all lowercase } } catch (Exception aEx) { ; } } } return wordsInFile.contains(word.toLowerCase()); }
Advertisement
Answer
This is a variant of @Stewart answer. You can handle lazy load without synchronize
by making use of class loader for DictionaryHolder
. This works because JVM won’t execute the static initialiser of a class multiple times if two threads called methods of that class at same time:
public class WordFinder { private static class DictionaryHolder { private static final Set<String> words; static { System.out.println( "Loading words..." ); words = Set.of("hello","world"); //words = yourLoadCode(); System.out.println( "Words loaded..." ); } public static Set<String> dictionary() { return words; } } private static void justDoSomething() { System.out.println( "Doing something..." ); } private static boolean IsFound(String word) { System.out.println( "Checking word " + word + "..." ); return DictionaryHolder.dictionary().contains( word ); } public static void main(String[] args) { WordFinder.justDoSomething(); WordFinder.IsFound( "foo" ); WordFinder.IsFound( "hello" ); } }
Note that the lazy loading class DictionaryHolder
does not have to be an inner class, it can be declared in any package outside. The above prints lines below confirming the lazy load on first call to IsFound("foo")
:
Doing something... Checking word foo... Loading words... Words loaded... Checking word hello...
It’s never a good idea to suppress exceptions. If you think throws IOException
is inappropriate, catch and re-throw as new RuntimeException(previousException)
so that callers are alerted to issues.