I have a store of data objects and I wish to synchronize modifications that are related to one particular 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 to say, I do not wish my data store to have a single lock since modifications to different data objects are completely orthogonal. Instead, I want to be able to take a lock that pertains to a single data object only.
Each data object has a unique id. One way is to create a map of ID => Lock and synchronize upon the one lock object associated with the id. Another way is to do something like:
synchronize(dataObject.getId().toString().intern()) {
// ...
}
However, this seems like a memory leak -- the internalized strings may never be collected.
Yet another idea is to synchronize upon the data object itself; however, what if you have an operation where the data object doesn't exist yet? For example, what will a method like addDataObject(DataObject) synchronize upon?
In summary, how can I write a function f(s), where s is a String, such that f(s)==f(t) if s.equals(t) in a memory-safe manner?
Add the lock directly to this DataObject, you could define it like this:
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 could 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 a single object locked for changes.
You could even make this a read-write lock if you also have read operations happening while writting.
For such case, I normally have 2 level of lock:
First level as a reader-writer-lock, which make sure update to the map (add/delete) is properly synchronized by treating them as "write", and access to entries in map is considered as "read" on the map. Once accessed to the value, then synchronize on the value. Here is a little 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();
}
}
}
Everything should then be properly synchronized without sacrificing much concurrency
Yet another idea is to synchronize upon the data object itself; however, what if you have an operation where the data object doesn't exist yet? For example, what will a method like addDataObject(DataObject) synchronize upon?
Synchronizing on the object is probably viable.
If the object doesn't exist yet, then nothing else can 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 don't need to synchronize it. Another approach is to partially initialize in the constructor, and then use synchronized methods to do the rest of the construction and the publication.
Related
I have the following classes:
class MyObjectManager {
Map<String, MyObject> myObjects;
void start(String myObjectName) {
// Create or reuse myObject for given name and run its a() method.
}
void stop(String myObjectName) {
// Create or reuse myObject for given name and run its b() method.
}
}
class MyObject {
void start() {
// do something
}
void stop() {
// do something
}
}
I need MyObject's start and stop methods to be ran synchronously for every MyObject instance and in the same order that MyObjectManager's methods were called with that myObjectName. However, I don't care about the order in which different MyObjects instances' methods are called. Initially, the map is empty and I create every MyObject the first time any of MyObjectManager's methods is called with specific myObjectName.
I think this can be solved by operating a master lock that synchronizes operations with myObjects map such as checking, adding and retrieving MyObjects instances. But after I obtained a MyObject instance either by retrieving it from the map or by creating it I need to lock on MyObject instance and release the master lock so that it's not kept unnecessarily locked while I execute MyObject.a() or MyObject.b().
I'm used to placing unlocking in finally blocks, so I imagine the code to be something like
void start(String myObjectName) {
MyObject myObject = null;
try {
synchronized(myObjects) {
myObject = createOrRetrieveMyObject(myObjectName);
if (myObject == null) {
// It's possible that myObjectName is invalid and MyObject will not be created.
return;
}
myObject.lock();
}
myObject.start();
} finally {
if (myObject != null) {
myObject.unlock();
}
}
}
That doesn't look pretty. Is there a better way?
I need MyObject's start and stop methods to be ran synchronously for every MyObject instance ...
I think that it might be more convenient do implement this synchronization logic inside MyObject.start() and MyObject.stop() (for instance you can make these methods synchronized), instead of requirement to use lock()+unlock() every time someone calls these methods.
I have a Set with any type of values and an AtomicBoolean that indicates if the functionality provided by that class is running.
private Set<Object> set = new HashSet<>();
private AtomicBoolean running;
Now, i have two methods, one of them is adding objects to the set and the other serves as a setup method for my class.
public void start() {
// ...
set.foreEach(someApi::addObject);
// ...
running.set(true);
}
public void addObject(Object o) {
set.add(o);
if(running.get()) {
someApi.addObject(o);
}
}
However, there is a problem with that code. If the method is invoked from another thread while the start method is iterating through the set running is still false. Thus, the object will not be added to the api.
Question: How can i guarantee that all objects in the set and objects added with addObject will be added to the api exactly one time?
My ideas:
use a lock and block the addObject method if the setup is currently adding methods to the api (or make both methods synchronized, which will slightly decrease performence tough)
Question: How can i guarantee that all objects in the set and objects added with addObject will be added to the api exactly one time?
You have to be careful here because this gets close to the ole "double check locking bug".
If I understand you question you want to:
queue the objects passed into addObject(...) in the set before the call to start().
then when start() is called, call the API method on the objects in the set.
handle the overlap if additional objects are added during the call to start()
call the method once and only once on all objects passed to addObject(...).
What is confusing is that your API call is also named addObject(). I assume this is different from the addObject(...) method in your code sample. I'm going to rename it below to be someApiMethod(...) to show that it's not going recursive.
The easiest way is going to be, unfortunately, having a synchronized block in each of the methods:
private final Set<Object> set = new HashSet<>();
public void start() {
synchronized (set) {
set.forEach(someApi::someApiMethod);
}
}
public void addObject(Object obj) {
synchronized (set) {
if (set.add(obj)) {
someApi.addObject(obj);
}
}
}
}
To make it faster is going to take a lot more complicated code. One thing you could do is use a ConcurrentHashMap and a AtomicBoolean running. Something like:
private final ConcurrentMap<Object, Object> map = new ConcurrentHashMap<>();
private final Set<Object> beforeStart = new HashSet<>();
private final AtomicBoolean running = new AtomicBoolean();
public void start() {
synchronized (beforeStart) {
for (Object obj : beforeStart) {
doIfAbsent(obj);
}
running.set(true);
}
}
public void addObject(Object obj) {
if (running.get()) {
doIfAbsent(obj);
} else {
synchronized (beforeStart) {
// we have to test running again once we get the lock
if (running.get()) {
doIfAbsent(obj);
} else {
beforeStart.add(obj);
}
}
}
}
private void doIfAbsent(Object obj) {
if (map.putIfAbsent(obj, obj)) {
someApi.someApiMethod(obj);
}
}
This is pretty complicated and it may not be any faster depending on how large your hash map is and other factors.
I've come across this particular scenario many times, and I wonder what's the "clean" way of solving it. It all comes to this: how can I store a reference to an object that's being set in a different Thread?
Let me illustrate this with an example, imagine I have a class named Bar, and objects from this class are retrieved from this method:
public class BarBuilder {
public static void buildNewBar(final BarListener listener) {
// This could be an HTTP request or something that can only be done in a
// different thread
new Thread(new Runnable() {
#Override
public void run() {
listener.onNewBar(new Bar());
}
}).start();
}
}
The important part here is that buildNewBar() method has to be executed in another Thread, so instead of returning the value, it will communicate the result through a listener. This is quite common for operations that need HTTP requests or any sort of connection.
Now, my problem is if I need the value before continuing execution, how can I access to it? I can lock a thread with a semaphore until I have my value, but the storing of the value is what I don't have clear (If I declare a final variable, it cannot be set again). I solved it creating a new class which I named "Pointer", but I wonder why there isn't any built in java class to do this (I used Vector before, but it doesn't seem like a good solution either).
public Bar getBar() {
final Pointer<Bar> barPointer = new Pointer<Bar>();
final Semaphore semaphore = new Semaphore(0);
BarBuilder.buildNewBar(new BarListener() {
#Override
public void onNewBar(Bar bar) {
barPointer.set(bar);
semaphore.release();
}
});
semaphore.acquireUninterruptibly();
// Now I have my value
return barPointer.get();
}
public class Pointer<T> {
T object;
public void set(T object) {
this.object = object;
}
public T get() {
return object;
}
}
Let's see if there is a better way of doing this supported by Java language, I have seen classes like Reference, but it seems like their purpose is something different and setters don't exist (they are read-only), so that doesn't solve my issues either.
public Bar getBar() {
final BarPointer barPointer = new BarPointer().
BarBuilder.buildNewBar(barPointer);
return barPointer.get();
}
public class BarPointer extends FutureTask<Bar> implements BarListener {
#Override
public void onNewBar(Bar bar) {
set(bar);
}
}
In order to eliminate the need to write a custom Pointer class, I would simply use AtomicReference.
I am aware that there are already a lot of similar questions on the internet, but my question is about my code, NOT ABOUT THREADS. I am making a small app that has a database of players. The code of the data storing class is as follows.
public class DataManager
{
static final int NO_OF_COLUMNS = 18;
static QDatabase pdb;
public DataManager()
{
pdb = new QDatabase(NO_OF_COLUMNS);
}
public void addPlayer(Object[] playerData)
{
pdb.add(playerData);
}
public void editPlayerInfo(int type, int playerRegNo, Object data)
{
pdb.set(type, playerRegNo, data);
}
public int getPlayerRegNo(String userID)
{
return (int) pdb.getData(USER_ID, userID, REG_NO);
}
public Boolean contains(int column, Object data)
{
return pdb.contains(column, data);
}
}
I have a server which keeps on recieving requests from multiple clients and creating a new thread for each of them. They all access this DataManager class which essentially acts as a database. Will it be possible for me to, in some way, make it possible for all the threads to be able to call the addPlayer() and editPlayerInfo() methods at the same time and yet not mess the whole thing up due to synchronization problems?
I also know that I can use databases. But here, I just thought that this would be easier. Assume that there will be about 200 threads running simultaneously. What is the best way for me to solve this?
Is there any way for me to hve all the threads access it at the same time as otherwise having 200 threads to wait on each other might become very slow?
EDIT 1:
The QDatabase class is as follows:
public class QDatabase implements Serializable
{
private ArrayList<ArrayList<Object>> database;
public final int NOT_EXISTS = 0, REGULAR = 0, TRANSPOSE = 1;
private int lastid = -1;
//Initializer taking the number of columns as an argument
public QDatabase(int noofcolumns)
{
database = new ArrayList<ArrayList<Object>>();
addColumns(noofcolumns);
}
//Method that adds an array of objects as a new row in the database.
public void add(Object[] object)
{
for(int index = 0; index < database.size(); index++)
{
if(object != null)
{
database.get(index).add(object[index]);
lastid = database.get(0).indexOf(object[0]);
}
}
}
//Method that finds the row in a column where an instance of a particular object is found and get the values at a
//cell with the same row and a given column.
public Object getData(int columntocheck, Object check, int columntoget)
{
Object ramobject = null;
int loc = database.get(columntocheck).indexOf(check);
ramobject = database.get(columntoget).get(loc);
return ramobject;
}
//Method to check if a column contains an instance of a given object.
public Boolean contains(int column, Object objecttocheck)
{
return database.get(column).contains(objecttocheck);
}
//Method to set a given cell to an object.
public void set(int column, int row, Object object)
{
database.get(column).set(row, object);
}
}
QDatabase is not thread-safe. You need either synchronize all its methods or use a thread-safe variant of ArrayList - CopyOnWriteArrayList from java.util.concurrent package. But be careful, using CopyOnWriteArrayList makes sense only if the number of reads from DB vastly outnumbers the number of writes. See API, it creates a fresh copy of unerlying array on all mutative operations.
UPDATE:
Actually, the most efficient solutiion in your situation seems to be ReadWriteLock. Use ReadLock for all reading operations and WriteLock for all mutative operations, like this
public class QDatabase implements Serializable {
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
...
public void add(Object[] object) {
writeLock.lock();
try {
...
}
} finally {
writeLock.unlock();
}
}
public Object getData(int columntocheck, Object check, int columntoget) {
readLock.lock();
try {
...
} finally {
readLock.unlock();
}
}
...
just add synchronized block
public synchronized void addPlayer(Object[] playerData)
{
pdb.add(playerData);
}
public synchronized void editPlayerInfo(int type, int playerRegNo, Object data)
{
pdb.set(type, playerRegNo, data);
}
It will make sure that no two threads will access this method at the same time.
One of the ways to have simultaneous access by multiple threads but yet remain thread safe is to use local variables or use ThreadLocal.
None of them is feasible in your case, so you can't achieve simultaneous access of thread, it has to be sequential.
Have a look at the java.util.concurrency package. You could use classes there to manage your threading needs better.
In order for a class/ method to be "thread safe" it has to be designed so. Now, its not clear what the DATABASE object you have is doing internally but it does look like from the method names , that multiple threads ARE going to be an issue.
In order to INCREASE the number of threads , yet not keeping the ENTIRE method synchronized, look into the details of the add/edit methods implementations and , yes you would have to limit thread access to those lines of code that would cause issues.
You could use principles like multiple READ ,single WRITE locks etc.
Suppose I have the following:
public class Foo {
private ReadingList mReadingList = new ReadingList();
public ReadingList getReadingList() {
synchronized (mReadingList) {
return mReadingList;
}
}
}
If I try modifying the ReadingList object in two threads, the synchronization above won't help me, right?:
// Thread 1
foo1.getReadingList().setName("aaa");
// Thread 2
foo2.getReadingList().setName("bbb");
do I have to wrap each method I want synchronized like so:
public class Foo {
private ReadingList mReadingList = new ReadingList();
public synchronized void setReadingListName(String name) {
mReadingList.setName(name);
}
public synchronized void setReadingListAuthor(String author) {
mReadingList.setAuthor(author);
}
...
and so on for each method of ReadingList I want exposed and synched? I'd end up just writing wrapper methods for each method of ReadingList.
Thanks
1. You have access to the ReadingList source
If you have access to the ReadingList object, add synchronized to all of the methods of ReadingList if you desire synchronized access to all of the fields or a certain group of setters if you only wish to interleave access to certain fields.
2. You do not have access to ReadingList
You would have to write something like:
public class Foo {
private ReadingList mReadingList = new ReadingList();
public void setReadingListName(String name) {
synchronized(mReadingList) {
mReadingList.setName(name);
}
}
public void setReadingListAuthor(String author) {
synchronized(mReadingList) {
mReadingList.setAuthor(author);
}
}
...
3. Use a general purpose lock object
Depending on the nature of Foo and how general-purpose this whole thing is, you may find that only a certain class or classes present the threading issue in ReadingList.
In such a class you could use a general purpose lock object:
public class Bar {
Object readingListLock = new Object();
public void someMethodThatModifiesReading() {
synchronized(readingListLock) {
foo.getReadingList().setName("1");
}
}
public void someOtherMethodThatModifiesReading() {
synchronized(readingListLock) {
foo.getReadingList().setName("2");
}
}
...
}
A quick (and not so efficient solution) is to synchronize all methods of ReadingList in ReadingList's implementation.
Look for Reader-Writer lock for a more efficient way to synchronize access: it allows multiple reads and single write at a time.
Your first solution only makes sure one thread gets the ReadingList at a time, and nothing else - many threads can read and modify the ReadingList concurrently.