Implementing "canonical" lock objects

I have a data warehouse and I want to synchronize the changes associated with one specific object at a time.

class DataStore { Map<ID, DataObject> objects = // ... // other indices and stuff... public final void doSomethingToObject(ID id) { /* ... */ } public final void doSomethingElseToObject(ID id) { /* ... */ } } 

That is, I do not want to have one lock in my data warehouse, since changes in different data objects are completely orthogonal. Instead, I want to be able to block, which applies to only one data object.

Each data object has a unique identifier. One way is to create an ID => Lock card and synchronize with one lock object associated with the identifier. Another way is to do something like:

 synchronize(dataObject.getId().toString().intern()) { // ... } 

However, this is similar to a memory leak - inline strings can never be collected.

Another idea is to synchronize with the data object itself; however, what if you have an operation when the data object does not yet exist? For example, how will a method such as addDataObject(DataObject) be synchronized?

So, how can I write a function f(s) , where s is a String , so that f(s)==f(t) if s.equals(t) memory-safe way?

+3
source share
3 answers

In this case, I usually have 2 lock levels: The first level is as a read / write lock, which ensures that the map update (add / remove) is correctly synchronized, treating them as a “write”, and access to the records on the card is considered “read” on the map. After accessing the value, then synchronize the value. Here is a small example:

 class DataStore { Map<ID, DataObject> objMap = // ... ReadWritLock objMapLock = new ReentrantReadWriteLock(); // other indices and stuff... public void addDataObject(DataObject obj) { objMapLock.writeLock().lock(); try { // do what u need, u may synchronize on obj too, depends on situation objMap.put(obj.getId(), obj); } finally { objMapLock.writeLock().unlock(); } } public final void doSomethingToObject(ID id) { objMapLock.readLock().lock(); try { DataObject dataObj = this.objMap.get(id); synchronized(dataObj) { // do what u need } } finally { objMapLock.readLock().unlock(); } } } 

Then everything should be correctly synchronized without sacrificing a lot of concurrency

+1
source

Add the lock directly to this DataObject, you can define it as follows:

 public class DataObject { private Lock lock = new ReentrantLock(); public void lock() { this.lock.lock(); } public void unlock() { this.lock.unlock(); } public void doWithAction( DataObjectAction action ) { this.lock(); try { action.doWithLock( this ) : } finally { this.unlock(); } } // other methods here } public interface DataObjectAction { void doWithLock( DataObject object ); } 

And when using it, you can simply do it like this:

 DataObject object = // something here object.doWithAction( new DataObjectAction() { public void doWithLock( DataObject object ) { object.setProperty( "Setting the value inside a locked object" ); } } ); 

And there you have one object locked for changes.

You can even do this by blocking reading and writing, if you also have read operations while writing.

+3
source

Another idea is to synchronize with the data object itself; however, what if you have an operation when the data object does not yet exist? For example, how will a method such as addDataObject (DataObject) be synchronized?

Object synchronization is probably viable.

If the object does not exist yet, nothing else will see it. Provided that you can arrange that the object is fully initialized by its constructor, and that it is not published by the constructor before the constructor returns, then you do not need to synchronize it. Another approach is to partially initialize the constructor, and then use synchronized methods to execute the rest of the construct and publish.

+1
source

Source: https://habr.com/ru/post/897586/


All Articles