Using synchronized methods set and get in the Metadata class is fine but may slower your web app in case you have multiple readers and (much) less writers:
Java synchronized keyword is used to acquire a exclusive lock on an
object. When a thread acquires a lock of an object either for reading
or writing, other threads must wait until the lock on that object is
released. Think of a scenerio that there are many reader threads that reads a shared
data frequently and only one writer thread that updates shared data.
It’s not necessary to exclusively lock access to shared data while
reading because multiple read operations can be done in parallel
unless there is a write operation.
(Excerpt from that nice post)
So using a multiple read single write strategy may be better in term of performance in some cases as explained also in the same Java5 ReadWriteLock interface doc:
A read-write lock allows for a greater level of concurrency in
accessing shared data than that permitted by a mutual exclusion lock.
It exploits the fact that while only a single thread at a time (a
writer thread) can modify the shared data, in many cases any number of
threads can concurrently read the data (hence reader threads). In
theory, the increase in concurrency permitted by the use of a
read-write lock will lead to performance improvements over the use of
a mutual exclusion lock. In practice this increase in concurrency will
only be fully realized on a multi-processor, and then only if the
access patterns for the shared data are suitable.
Whether or not a read-write lock will improve performance over the use
of a mutual exclusion lock depends on the frequency that the data is
read compared to being modified, the duration of the read and write
operations, and the contention for the data - that is, the number of
threads that will try to read or write the data at the same time. For
example, a collection that is initially populated with data and
thereafter infrequently modified, while being frequently searched
(such as a directory of some kind) is an ideal candidate for the use
of a read-write lock. However, if updates become frequent then the
data spends most of its time being exclusively locked and there is
little, if any increase in concurrency. Further, if the read
operations are too short the overhead of the read-write lock
implementation (which is inherently more complex than a mutual
exclusion lock) can dominate the execution cost, particularly as many
read-write lock implementations still serialize all threads through a
small section of code. Ultimately, only profiling and measurement will
establish whether the use of a read-write lock is suitable for your
application.
A ready to use implementation is the ReentrantReadWriteLock.
Take a look at the previous post for a nice tutorial on how to use it.