I use these methods, 1 or 2 below in various projects.
1. Use java.nio.file.WatchService to wait for changes.
I guess this is what you mean by "a listener".
For the narrow scenario of tracking occasional changes to a few config files on Linux local filesystems (using Linux's inotify), this produces very quick updates (near instant from a user perspective) when a config file changes, with insignificant resources used, in my experience.
Linux's inotify is not a polling mechanism. It's a feature of the linux kernel that generates events when files change. Java has wrapped this with the WatchService. Windows has similar capabilities and I imagine your JDK/JVM will use them, but I have not tested it.
In my experience it does not take 3x time or cpu to track config local file changes in this way. But it's not perfect. In particular renaming of files and directories can be tricky (not part of my use scenarios) and inotify does not pick up change events on remote filesystems. WatchService falls back on other implementation (probably polling), which I have not used.
I can't swear to the implementation on every platform for every JDK. Test it.
2. Implement a simple form of lazy initialization with caching.
This produces reliable cheap updating when config files change, with a defined maximum delay period, like 30s.
For the first request read the file, save a timestamp.
For every subsequent request of the file's properties, check the timestamp.
If the data is older than your cache expiration time (say 30s or 1 minute), check the file's modify time and reread if needed.
For both approaches to share configuration across a busy multithreaded application, I use standard tools from java.util.concurrent to allow non-blocking reads of configuration. For Example:
Make the properties structure immutable. Share access to it through an AtomicReference<>. When you reread the config file make a new instance of your config properties and replace the old instance.
Or Use a ConcurrentHashMap referencing immutable objects, if the contents consist of an extensible number of different objects needing separate updating and key-value access. When rereading a config file, replace objects with new immutable instances.