I have a synchronized method that is being called from a controller method.
When two request access this only one should go through and other should be blocked until first is finished.
But when the incoming requests are fast, this is actually returning same accountId to two different requests, which is not intended.
Please help me understand how do I synchronize this getNextAccount() call so that it only return one account to one request.
Method in AccService.java
private final Object lockObject = new Object();
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Long getNextAccount(String hostport) {
synchronized (lockObject) {
Long acc = null;
try {
AccountFetch dtls = getAccount();
if (dtls != null) {
acc = dtls.getAccId();
//Set IN_PROGRESS
dtls.setStatus("Progress");
dtls.saveAndFlush(dtls);
return acc;
} else {
log.info("No More Accounts to Process");
}
} catch (Exception e) {
e.getStackTrace();
}
return acc;
}
}
#Autowired
private AccService accSevice;
#GET
#Path("/accprocess")
#Produces(MediaType.APPLICATION_JSON)
public AccountFetch getAccId(#QueryParam("currentHost") final String currentHost,
#QueryParam("currentPort") final String currentPort) {
AccountFetch dtls = new AccountFetch();
try {
Long batchId = accSevice. getNextAccount(currentHost+"#"+currentPort);
if (accId != null) {
dtls.setAccId(String.valueOf(accId));
} else {
dtls.setAccId(BLANK_STRING);
}
} catch (Exception e) {
log.error("Exception while getting accId : " + e.getMessage());
}
return dtls;
}
public AccountFetch getAccount(){...}
A synchronized block will only give you mutual exclusion if the threads are on the same host, and if they are locking using the same lock object.
Based on what you originally wrote in your question, it seems that one or both of these preconditions is not satisfied. If (as it now transpires) there is only one host processing these requests, then we must consider the other possibility; i.e. that there multiple instances of the AccService object processing requests.
It is also possible that synchronization is working, and the problem is somewhere else. For example, getAccount() could be returning the same account on successive calls.
Basically, there are too many parts of your code-base that we can't see. This means that we can only theorize as to what is causing the problem. If every thing was done correctly; i.e.
there is only one host,
all threads are sharing the same lock object,
getAccount() is implemented correctly, and
nothing else updates the account state used by getAccount without proper synchronization,
then the code you have shown us would work.
If you need more help, an MCVE is probably required.
Related
I've been trying to learn more about thread safety and have been trying to think of good practices when implementing thread safe code. In the context of token retrieval, I want to keep track of the current time, and the time the token will expire (divided in half, so I will refresh in advance of the token expiring). Here's some pseudo code on the situation I'm thinking about
long renewTime = 0; //should this be a volatile long?
Lock lock = new Lock
Service someService = new Service();
public void authenticateWithSomeService() {
try {
lock.get()
//...call to login with someService
renewTime = System.currentTimeMillis() + someService.getToken.getExpirationTime/2
catch (Exception e) {
//log or rethrow
} finally {
lock.unlock();
}
}
private void refreshToken() {
if(System.currentTimeMillis() > renewTime) {
//...call to renew token with someService
long newTime = someService.getToken.getExpirationTime/2
renewTime = System.currentTimeMillis() + newTime;
}
}
public synchronized String getStuff() {
refreshToken(); //check if token needs to be refreshed
try {
//...some HTTP call to get an item with the service
} catch (Exception e) {
//log or rethrow
}
}
So in a scenario like this, are we guarenteed that renewTime will only ever be read/written by one thread at a time since 1. the authentication method employs locks and 2. the getStuff method which read and writes renewTime is synchronized? Is this threadsafe or does renewTime need to be volatile to ensure the threads access the same value?
So I have multiple Java Objects and at some time during the execution a thread will ask for its ID. When it does that for the first time I Want to make sure it also starts storing the object session in a database, however I don't want to lock the whole object during the database call, just the thread that's currently calling it.
Here's the current code I'm using, will that work?
public String getId() {
boolean shouldAddTaskToSql = false;
synchronized (this) {
if(!this.hasBeenAddedToSql) {
shouldAddTaskToSql = true;
this.hasBeenAddedToSql = true;
}
}
if(shouldAddTaskToSql) {
try {
new MySQLTaskCreateSession().runQuery(new Object[] { this });
} catch (SQLException e) {
e.printStackTrace();
} catch (QueryCanceledException e) { }
}
return this.state_id;
}
What I'm trying to accomplish is not making multiple threads lock because a single one is writing the session to a databse, I need multiple threads to be able to call getId() while only making one of them insert to the database. (while the db call is being made other threads can still call getId() without waiting for the synchronized lock to release.
I have the following two methods in a class:
private MyDef myDef;
private FutureTask<MyDef> defFutureTask;
public synchronized void periodEviction() {
myDef = null;
}
public MyDef loadMyItems() {
// if it's not ready use a future - it will block until the results are ready
if (this.myDef == null) { // this will still not be thread safe
Callable<MyDef> callableDef = ()->{ return this.loadFromDatabase(); };
FutureTask<MyDef> defTask = new FutureTask<>(callableDef);
this.defFutureTask = defTask;
defFutureTask.run();
}
try {
// wait until's it's ready
this.myDef = this.qDefFuture.get();
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return this.myDef;
}
I wanted to do the following:
1) Do a cache eviction using periodEviction() every one hour or so.
2) Otherwise, use the cached value when db loading is done.
I believe I have misunderstood Java future as I couldn't answer the question, "What happens when Thread A,B,and C all are calling loadMyItems() at the same time?"
So does this mean without something like an executor, this implementation is still not thread safe?
An even simpler approach is to not cache the object at all but just retain the Future.
private CompletableFuture<MyDef> defFuture;
public synchronized void periodEviction() {
// evict by triggering the request anew
defFuture = CompletableFuture.supplyAsync(this::loadFromDatabase);
}
public synchronized Optional<MyDef> loadMyItems() {
try {
return Optional.of(this.defFuture.get());
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return Optional.empty();
}
With the caveat that this will trigger the database query every eviction period rather than on demand.
A super simple approach would be to declare loadMyItems as synchronized. But if the class has other methods that access myDef, you would have to declare those synchronized too. Sometimes this results in very coarse-grained locking and slower performance.
If you're looking for the cleanest/fastest code, instead of declaring periodEviction as synchronized, declare myDef as an AtomicReference:
private final AtomicReference<MyDef> myDef = new AtomicReference<>();
Then the body of periodEviction is:
synchronized (myDef) {
myDef.set(null);
}
And the body of loadMyItems is:
synchronized (myDef) {
if (myDef.get() == null) {
// perform initialization steps, ending with:
myDef.set(this.qDefFuture.get());
}
return myDef.get();
}
If many threads call loadMyItems at the same time, myDef will only ever be initialized once, and they will all get the same object returned (unless somehow a call to periodEviction snuck in the middle).
When working with a BlockingQueue, i implemented the following logic to read from it until told otherwise. Unfortunately the following is happening, intermittently:
The problem:
Even after shouldContinueReading is set to false, loop does not CONSISTENTLY break
The problem is intermittent, sometimes everything works fine
As part of the QThread class, i declare:
public static volatile boolean shouldContinueReading = true;
Run (confirmed to be executing) method contains:
while (shouldContinueReading) {
try {
String retrieved = qIn.poll(2, TimeUnit.MILLISECONDS);
if (retrieved != null)
consume(retrieved);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("I am out"); // <-- not always seen
if (qIn.remainingCapacity() > 0) {
try {
consume(qIn.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
While this is going on, in another thread, when certain things happen, shouldContinueReading changes state
while (stillReading) {
// do nothing
}
QThread.shouldContinueReading = false;
Update: problem resolved
Turns out the problem lies a bit further:
private void consume(String take) {
// some processing
produce(newData.toString());
}
private void produce(String newData) {
System.out.println(newData);
try {
qOut.put(newData); // <-- Problem is here. Should use offer instead of put
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Both qIn (queue in) and qOut (queue out) are declared as:
private volatile BlockingQueue<String> qIn;
private volatile BlockingQueue<String> qOut;
The objects themselves are created elsewhere as follows and passed down to the constructor:
BlockingQueue<String> q1 = new SynchronousQueue<String>();
BlockingQueue<String> q2 = new SynchronousQueue<String>();
QThread qThread = new QThread(q1, q2);
Any suggestions? what i should do with qOut? Am i not declaring it correctly?
I bet QThread.shouldContinueReading = false; isn't getting executed always,or the reading thread is not executing in the first place. I.e. the problem you are seeing is likely somewhere up the stream -- not here. The first thing I'd do would be to pin down where exactly the problem lies, with 100% confidence (put some more print statements).
Apart from the problem, I'd recommend to use the thread interruption mechanism instead of rolling your own flag (which is, in turn just a glorified flag, but that way you can affect third party codes like BlockedQueue and make the implementation simpler and more efficient even) especially if this is production code.
I connect to an external service with an interactive session + a private feed (InputStream) that run on separate threads. On the interactive session, I send outgoing messages and receive synchronous responses with an object containing different fields, one being an ID and a 'status' confirming success or failure. Simultaneously I receive messages on the private feed for this ID with further 'status' updates. I currently store information about the status per ID in a ConcurrentHashMap. It is imperative that I keep a correct sequence of events on these objects but I am currently getting race conditions where I sometimes process and update the objects on the private feed before I receive and process the synchronous response on the interactive session, hence leaving me with an obsolete and incorrect status for the ID.
Ideally, I would have liked to have some type of collection with a PutIfKeyExistOrWait (w timeout) method, that would only update the value if the key exists or else wait, that I could use when processing objects on the private feed.
Does anyone know if there is a suitable collection available or can suggest an alternative solution to my problem? Thanks.
You can try to encapsulate logic for handling this situation into values of your map, something like this:
If feed thread is the first to add a value for particular id, that value is considered incomplete and thread waits until it's completed
If interactive session thread isn't the first to add a value, it marks that incomplete value as complete
Incomplete values are treated as absent when getting them from the map
This solution is based on atomicity of putIfAbsent().
public class StatusMap {
private Map<Long, StatusHolder> map = new ConcurrentHashMap<Long, StatusHolder>();
public Status getStatus(long id) {
StatusHolder holder = map.get(id);
if (holder == null || holder.isIncomplete()) {
return null;
} else {
return holder.getStatus();
}
}
public void newStatusFromInteractiveSession(long id, Status status) {
StatusHolder holder = StatusHolder.newComplete(status);
if ((holder = map.putIfAbsent(id, holder)) != null) {
holder.makeComplete(status); // Holder already exists, complete it
}
}
public void newStatusFromFeed(long id, Status status) {
StatusHolder incomplete = StatusHolder.newIncomplete();
StatusHolder holder = null;
if ((holder = map.putIfAbsent(id, incomplete)) == null) {
holder = incomplete; // New holder added, wait for its completion
holder.waitForCompletion();
}
holder.updateStatus(status);
}
}
public class StatusHolder {
private volatile Status status;
private volatile boolean incomplete;
private Object lock = new Object();
private StatusHolder(Status status, boolean incomplete) { ... }
public static StatusHolder newComplete(Status status) {
return new StatusHolder(status, false);
}
public static StatusHolder newIncomplete() {
return new StatusHolder(null, true);
}
public boolean isIncomplete() { return incomplete; }
public void makeComplete(Status status) {
synchronized (lock) {
this.status = status;
incomplete = false;
lock.notifyAll();
}
}
public void waitForCompletion() {
synchronized (lock) {
while (incomplete) lock.wait();
}
}
...
}
You already have some ConcurrentHashMap iDAndStatus that stores the ID and latest status. However, I would only let the thread that deals with the service create a new entry in that map.
When a message arrives from the feed, if the ID already exists in iDAndStatus, it just modifies the status. If the key does not exist, just store temporarily the ID/status updates in some other data structure, pendingFeedUpdates.
Everytime a new entry is created in iDAndStatus, check pendingFeedUpdates to see if some update(s) for the new ID are present.
I'm not sure what synchronized data structure to use for pendingFeedUpdates: you need to retrieve by ID, but you might have many messages for each ID, and you want to keep the order of the messages. Maybe a synchronized HashMap that associates each ID with some type of synchronized ordered Queue?
I would suggest you look at the Collections.getSynchronized collection:http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#synchronizedList%28java.util.List%29
This could maybe solve you problem the other option depending how the calls are made have the method be a synchronized method that allows for thread safe execution and would ensure atomicity of transaction. See http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
The third option is to enforce a concurrency management control within the application following an optimistic or pessimistic approach depending on what you are trying to achieve. This is the most complex of the 3 but will give you the greater control if coupled with the previous options.
This is really dependent on your specific implementation.