Basically, I have a class with 2 methods: one to serialize an object into an XML file and another to read an object from XML. Here is an example of synchronized part from the method that restores an object:
public T restore(String from) throws Exception { // variables declaration synchronized (from) { try { decoder = new XMLDecoder(new BufferedInputStream( new FileInputStream(from))); restoredItem = decoder.readObject(); decoder.close(); } catch (Exception e) { logger.warning('file not found or smth: ' + from); throw new Exception(e); } } // try to cast it }
A similar approach is taken when serializing an object. Now, when I create a unit test which in turn creates 10 threads with each thread trying to serialize and instantly read either a Boolean or a String it would fail showing that ClassCastExceptions occur. This makes me think that I get serialization wrong (everything’s ok in a single-threaded environment). If you’ve stayed with me down to this point :), here are the 2 issues I need your help on:
- does it make sense to synchronize on a string argument passed to method (considering there’s a string pool in java)? BTW, I’ve tried synchronizing on the XMLSerializer class itself with result staying the same.
- how do i correctly synchronize io operations on a single file?
1. Yes, it’s OK to synchronize on a String, however you’d need to synchronize on the string.intern() in order to always get the same Object
Since you want to synchronize on the same monitor, you’d want the intern().
2. You’d probably not want to synchronize on a String since it may synchronized on somewhere else, inside your code, in 3rd party or in the JRE. What I’d do, if I wanted to stay with synchronize, is create an ID class (which may hold only String), override equals() and hashcode() to match, put it in a WeakHashMap with both as key and value (see IdentityHashMap for idea why) and use only what I .get() from the map (sync map{ syncKey = map.get(new ID(from)); if syncKey==null create and put new key} sync{syncKey}).
3. Then again, I’d ditch synchronize all together and use the java.util.concurrent.locks.Lock instead, in the same setup as above only with the lock attached to the ID.