Class ConcurrencyManager
- java.lang.Object
-
- org.eclipse.persistence.internal.helper.ConcurrencyManager
-
- All Implemented Interfaces:
java.io.Serializable
- Direct Known Subclasses:
CacheKey
public class ConcurrencyManager extends java.lang.Object implements java.io.SerializableINTERNAL:Purpose: To maintain concurrency for a particular task. It is a wrappers of a semaphore that allows recursive waits by a single thread.
Responsibilities:
- Keep track of the active thread.
- Wait all other threads until the first thread is done.
- Maintain the depth of the active thread.
- See Also:
- Serialized Form
-
-
Field Summary
Fields Modifier and Type Field Description protected java.lang.ThreadactiveThreadstatic java.util.Map<java.lang.Thread,DeferredLockManager>DEFERRED_LOCK_MANAGERSprotected java.util.concurrent.atomic.AtomicIntegerdepthprotected booleanlockedByMergeManagerprotected java.util.concurrent.atomic.AtomicIntegernumberOfReadersprotected java.util.concurrent.atomic.AtomicIntegernumberOfWritersWaitingprotected static booleanshouldTrackStackprotected java.lang.Exceptionstack
-
Constructor Summary
Constructors Constructor Description ConcurrencyManager()Initialize the newly allocated instance of this class.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description voidacquire()Wait for all threads except the active thread.voidacquire(boolean forMerge)Wait for all threads except the active thread.voidacquireDeferredLock()Add deferred lock into a hashtable to avoid deadlockbooleanacquireIfUnownedNoWait(boolean forMerge)If the activeThread is not set, acquire it and return true.booleanacquireNoWait()If the lock is not acquired already acquire it and return true.booleanacquireNoWait(boolean forMerge)If the lock is not acquired already acquire it and return true.voidacquireReadLock()Wait on any writer.booleanacquireReadLockNoWait()If this is acquired return false otherwise acquire readlock and return truebooleanacquireWithWait(boolean forMerge, int wait)If the lock is not acquired already acquire it and return true.protected voidaddReadLockToReadLockManager()The current thread has incremented the number of readers on the current cache key.voidcheckDeferredLock()Check the lock state, if locked, acquire and release a deferred lock.voidcheckReadLock()Check the lock state, if locked, acquire and release a read lock.static voidclearJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse()Clear the justification why theisBuildObjectOnThreadComplete(Thread, Map, List, boolean)is going nowhere.static voidenrichStringBuildingExplainWhyThreadIsStuckInIsBuildObjectOnThreadComplete(java.util.List<java.lang.Thread> chainOfThreadsExpandedInRecursion, ConcurrencyManager finalDeferredLockCausingTrouble, java.lang.Thread activeThreadOnDeferredLock, boolean hasDeferredLockManager, java.lang.StringBuilder justification)When the recursive algorithm decides to return false it is because it is confronted with a cache key that had to be deferred.java.lang.ThreadgetActiveThread()Return the active thread.java.util.DategetConcurrencyManagerCreationDate()Getter forconcurrencyManagerCreationDatelonggetConcurrencyManagerId()Getter forconcurrencyManagerIdstatic DeferredLockManagergetDeferredLockManager(java.lang.Thread thread)Return the deferred lock manager from the threadprotected static java.util.Map<java.lang.Thread,DeferredLockManager>getDeferredLockManagers()Return the deferred lock manager hashtable (thread - DeferredLockManager).intgetDepth()Return the current depth of the active thread.intgetNumberOfReaders()Number of writer that want the lock.intgetNumberOfWritersWaiting()Number of writers that want the lock.protected static ReadLockManagergetReadLockManager(java.lang.Thread thread)The method is not synchronized because for now we assume that each thread will ask for its own lock manager.protected static ReadLockManagergetReadLockManagerEnsureResultIsNotNull(java.lang.Thread thread)Same asgetReadLockManager(Thread)but in this case a not null result is ensuredprotected static java.util.Map<java.lang.Thread,ReadLockManager>getReadLockManagers()Return the deferred lock manager hashtable (thread - DeferredLockManager).java.lang.ExceptiongetStack()static java.util.Map<java.lang.Thread,ConcurrencyManager>getThreadsToWaitOnAcquire()Getter forTHREADS_TO_WAIT_ON_ACQUIREstatic java.util.Map<java.lang.Thread,java.lang.String>getThreadsToWaitOnAcquireMethodName()static java.util.Map<java.lang.Thread,ConcurrencyManager>getThreadsToWaitOnAcquireReadLock()Getter forTHREADS_TO_WAIT_ON_ACQUIRE_READ_LOCKstatic java.util.Map<java.lang.Thread,java.lang.String>getThreadsToWaitOnAcquireReadLockMethodName()static java.util.Set<java.lang.Thread>getThreadsWaitingToReleaseDeferredLocks()Getter forTHREADS_WAITING_TO_RELEASE_DEFERRED_LOCKSstatic java.util.Map<java.lang.Thread,java.lang.String>getThreadsWaitingToReleaseDeferredLocksJustification()longgetTotalNumberOfKeysAcquiredForReading()Getter fortotalNumberOfKeysAcquiredForReadinglonggetTotalNumberOfKeysReleasedForReading()Getter fortotalNumberOfKeysReleasedForReadinglonggetTotalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero()protected static java.util.MapinitializeDeferredLockManagers()Init the deferred lock managers (thread - DeferredLockManager).booleanisAcquired()Return if a thread has acquire this manager.static booleanisBuildObjectOnThreadComplete(java.lang.Thread thread, java.util.Map recursiveSet, java.util.List<java.lang.Thread> parentChainOfThreads, boolean deadLockDiagnostic)Check if the deferred locks of a thread are all released.booleanisLockedByMergeManager()INTERNAL: Used byt the refresh process to determine if this concurrency manager is locked by the merge process.booleanisNested()Return if this manager is within a nested acquire.voidputDeferredLock(java.lang.Thread thread, DeferredLockManager lockManager)voidputThreadAsWaitingToAcquireLockForReading(java.lang.Thread currentThread, java.lang.String methodName)The thread is trying to acquire a read lock but it is not being able to make process on getting the read lock.voidputThreadAsWaitingToAcquireLockForWriting(java.lang.Thread thread, java.lang.String methodName)Normally this mehtod should only be called from withing the concurrency manager.voidrelease()Decrement the depth for the active thread.voidreleaseAllLocksAcquiredByThread(DeferredLockManager lockManager)For the thread to release all of its locks.voidreleaseDeferredLock()Release the deferred lock.voidreleaseReadLock()Decrement the number of readers.static DeferredLockManagerremoveDeferredLockManager(java.lang.Thread thread)Remove the deferred lock manager for the threadprotected voidremoveReadLockFromReadLockManager()The current thread is about to decrement the number of readers in cache key.protected static voidremoveReadLockManagerIfEmpty(java.lang.Thread thread)Just like we see that the satic map of deffered locks is cleared of cache values for the current thread we also want to try to keep the static map of acquired read locks by a thread light weight by removing the association between the current thread and a read lock manager whenever the read lock manager becomes empty.voidremoveThreadNoLongerWaitingToAcquireLockForReading(java.lang.Thread thread)voidremoveThreadNoLongerWaitingToAcquireLockForWriting(java.lang.Thread thread)The thread has acquired the lock for writing or decided to defer acquiring the lock putting this lock into its deferred lock list.voidsetActiveThread(java.lang.Thread activeThread)Set the active thread.protected voidsetDepth(int depth)Set the current depth of the active thread.voidsetIsLockedByMergeManager(boolean state)INTERNAL: Used by the mergemanager to let the read know not to refresh this object as it is being loaded by the merge process.static voidsetJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse(java.lang.String justification)SeeclearJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse()in this case we want to store the justification computed by theenrichStringBuildingExplainWhyThreadIsStuckInIsBuildObjectOnThreadComplete(List, ConcurrencyManager, Thread, boolean, StringBuilder)protected voidsetNumberOfReaders(int numberOfReaders)Track the number of readers.protected voidsetNumberOfWritersWaiting(int numberOfWritersWaiting)Number of writers that want the lock.static voidsetShouldTrackStack(boolean shouldTrackStack)INTERNAL: This can be set during debugging to record the stacktrace when a lock is acquired.voidsetStack(java.lang.Exception stack)static booleanshouldTrackStack()java.lang.StringtoString()Print the nested depth.voidtransitionToDeferredLock()
-
-
-
Field Detail
-
DEFERRED_LOCK_MANAGERS
public static final java.util.Map<java.lang.Thread,DeferredLockManager> DEFERRED_LOCK_MANAGERS
-
shouldTrackStack
protected static boolean shouldTrackStack
-
numberOfReaders
protected java.util.concurrent.atomic.AtomicInteger numberOfReaders
-
depth
protected java.util.concurrent.atomic.AtomicInteger depth
-
numberOfWritersWaiting
protected java.util.concurrent.atomic.AtomicInteger numberOfWritersWaiting
-
activeThread
protected transient volatile java.lang.Thread activeThread
-
lockedByMergeManager
protected boolean lockedByMergeManager
-
stack
protected java.lang.Exception stack
-
-
Method Detail
-
acquire
public void acquire() throws ConcurrencyExceptionWait for all threads except the active thread. If the active thread just increment the depth. This should be called before entering a critical section.- Throws:
ConcurrencyException
-
acquire
public void acquire(boolean forMerge) throws ConcurrencyExceptionWait for all threads except the active thread. If the active thread just increment the depth. This should be called before entering a critical section. called with true from the merge process, if true then the refresh will not refresh the object- Throws:
ConcurrencyException
-
acquireNoWait
public boolean acquireNoWait() throws ConcurrencyExceptionIf the lock is not acquired already acquire it and return true. If it has been acquired already return false Added for CR 2317- Throws:
ConcurrencyException
-
acquireNoWait
public boolean acquireNoWait(boolean forMerge) throws ConcurrencyExceptionIf the lock is not acquired already acquire it and return true. If it has been acquired already return false Added for CR 2317 called with true from the merge process, if true then the refresh will not refresh the object- Throws:
ConcurrencyException
-
acquireWithWait
public boolean acquireWithWait(boolean forMerge, int wait) throws ConcurrencyExceptionIf the lock is not acquired already acquire it and return true. If it has been acquired already return false Added for CR 2317 called with true from the merge process, if true then the refresh will not refresh the object- Throws:
ConcurrencyException
-
acquireIfUnownedNoWait
public boolean acquireIfUnownedNoWait(boolean forMerge) throws ConcurrencyExceptionIf the activeThread is not set, acquire it and return true. If the activeThread is set, it has been acquired already, return false. Added for Bug 5840635 Call with true from the merge process, if true then the refresh will not refresh the object.- Throws:
ConcurrencyException
-
acquireDeferredLock
public void acquireDeferredLock() throws ConcurrencyExceptionAdd deferred lock into a hashtable to avoid deadlock- Throws:
ConcurrencyException
-
checkDeferredLock
public void checkDeferredLock() throws ConcurrencyExceptionCheck the lock state, if locked, acquire and release a deferred lock. This optimizes out the normal deferred-lock check if not locked.- Throws:
ConcurrencyException
-
checkReadLock
public void checkReadLock() throws ConcurrencyExceptionCheck the lock state, if locked, acquire and release a read lock. This optimizes out the normal read-lock check if not locked.- Throws:
ConcurrencyException
-
acquireReadLock
public void acquireReadLock() throws ConcurrencyExceptionWait on any writer. Allow concurrent reads.- Throws:
ConcurrencyException
-
acquireReadLockNoWait
public boolean acquireReadLockNoWait()
If this is acquired return false otherwise acquire readlock and return true
-
getActiveThread
public java.lang.Thread getActiveThread()
Return the active thread.
-
getDeferredLockManager
public static DeferredLockManager getDeferredLockManager(java.lang.Thread thread)
Return the deferred lock manager from the thread
-
getDeferredLockManagers
protected static java.util.Map<java.lang.Thread,DeferredLockManager> getDeferredLockManagers()
Return the deferred lock manager hashtable (thread - DeferredLockManager).
-
initializeDeferredLockManagers
protected static java.util.Map initializeDeferredLockManagers()
Init the deferred lock managers (thread - DeferredLockManager).
-
getDepth
public int getDepth()
Return the current depth of the active thread.
-
getNumberOfReaders
public int getNumberOfReaders()
Number of writer that want the lock. This is used to ensure that a writer is not starved.
-
getNumberOfWritersWaiting
public int getNumberOfWritersWaiting()
Number of writers that want the lock. This is used to ensure that a writer is not starved.
-
isAcquired
public boolean isAcquired()
Return if a thread has acquire this manager.
-
isLockedByMergeManager
public boolean isLockedByMergeManager()
INTERNAL: Used byt the refresh process to determine if this concurrency manager is locked by the merge process. If it is then the refresh should not refresh the object
-
isBuildObjectOnThreadComplete
public static boolean isBuildObjectOnThreadComplete(java.lang.Thread thread, java.util.Map recursiveSet, java.util.List<java.lang.Thread> parentChainOfThreads, boolean deadLockDiagnostic)Check if the deferred locks of a thread are all released. Should write dead lock diagnostic information into theTHREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS_BUILD_OBJECT_COMPLETE_GOES_NOWHERE.- Parameters:
thread- the current thread to be explored. It starts by being the thread that it is stuck but then it evolves to be other that have acquired locks our main thread was needing but whcich themslves are stuck... threads in the deffered lock chain that are going nowhere themselves.recursiveSet- this prevents the algorithm going into an infinite loop of expanding the same thread more than once.parentChainOfThreads- this starts by being a basket containing the current thread, but each time we go deeper it evolves to contain the thread we will explore next.- Returns:
- true if object is complete
-
enrichStringBuildingExplainWhyThreadIsStuckInIsBuildObjectOnThreadComplete
public static void enrichStringBuildingExplainWhyThreadIsStuckInIsBuildObjectOnThreadComplete(java.util.List<java.lang.Thread> chainOfThreadsExpandedInRecursion, ConcurrencyManager finalDeferredLockCausingTrouble, java.lang.Thread activeThreadOnDeferredLock, boolean hasDeferredLockManager, java.lang.StringBuilder justification)When the recursive algorithm decides to return false it is because it is confronted with a cache key that had to be deferred. And the cache key is either being owned by a thread that did not flage itsef as being finished and waiting in the wait for deferred locks. Or the thread that ows the cache key is not playing nice - and not using deferred locks - so it has acquire the cache key, it is going about its business (e.g. committing a transaction or perhaps doing object building. Normally, but not always, in object building threads do have a lock manager, but sometimes not when they agressive acquire lock policy. )- Parameters:
chainOfThreadsExpandedInRecursion- This the chaing threads that were expanded as we went down with the recursionfinalDeferredLockCausingTrouble- this is a lock that was deferred either by current thread or by a thread that is also itself waiting around . This lock is what is causing us ultimately to return FALSE, because the lock is still ACUIRED so not yet free. And the thread that owns it is also still not finished yet.activeThreadOnDeferredLock- this is the thread that was spotted as owning/being actively owning the the deferred lock. So we can consider this thread as being the ultimate cause of why the current thread and perhaps a hole chaing of related threads are not evolving. But certainly the current thread.hasDeferredLockManager- Some threads have deferred lock managers some not. Not clear when they do. But threads doing object building typically end up creating a deferred lock manager when they find themselves unable to acquire an object and need to defer on the cache key.justification- this is what we want to populate it will allow us to build a trace to explain why the thread on the wait for deferred lock is going nowhere. This trace will be quite important to help us interpret the massive dumps since it is quite typical to find threads in this state.
-
isNested
public boolean isNested()
Return if this manager is within a nested acquire.
-
putDeferredLock
public void putDeferredLock(java.lang.Thread thread, DeferredLockManager lockManager)
-
release
public void release() throws ConcurrencyExceptionDecrement the depth for the active thread. Assume the current thread is the active one. Raise an error if the depth become < 0. The notify will release the first thread waiting on the object, if no threads are waiting it will do nothing.- Throws:
ConcurrencyException
-
releaseDeferredLock
public void releaseDeferredLock() throws ConcurrencyExceptionRelease the deferred lock. This uses a deadlock detection and resolution algorithm to avoid cache deadlocks. The deferred lock manager keeps track of the lock for a thread, so that other thread know when a deadlock has occurred and can resolve it.- Throws:
ConcurrencyException
-
releaseReadLock
public void releaseReadLock() throws ConcurrencyExceptionDecrement the number of readers. Used to allow concurrent reads.- Throws:
ConcurrencyException
-
removeDeferredLockManager
public static DeferredLockManager removeDeferredLockManager(java.lang.Thread thread)
Remove the deferred lock manager for the thread
-
setActiveThread
public void setActiveThread(java.lang.Thread activeThread)
Set the active thread.
-
setDepth
protected void setDepth(int depth)
Set the current depth of the active thread.
-
setIsLockedByMergeManager
public void setIsLockedByMergeManager(boolean state)
INTERNAL: Used by the mergemanager to let the read know not to refresh this object as it is being loaded by the merge process.
-
setNumberOfReaders
protected void setNumberOfReaders(int numberOfReaders)
Track the number of readers.
-
setNumberOfWritersWaiting
protected void setNumberOfWritersWaiting(int numberOfWritersWaiting)
Number of writers that want the lock. This is used to ensure that a writer is not starved.
-
transitionToDeferredLock
public void transitionToDeferredLock()
-
releaseAllLocksAcquiredByThread
public void releaseAllLocksAcquiredByThread(DeferredLockManager lockManager)
For the thread to release all of its locks.- Parameters:
lockManager- the deferred lock manager
-
getReadLockManager
protected static ReadLockManager getReadLockManager(java.lang.Thread thread)
The method is not synchronized because for now we assume that each thread will ask for its own lock manager. If we were writing a dead lock detection mechanism then a ThreadA could be trying understand the ReadLocks of a ThreadB and this would no longer be true.- Parameters:
thread- The thread for which we want to have look at the acquired read locks.- Returns:
- Never null if the read lock manager does not yet exist for the current thread. otherwise its read log manager is returned.
-
getReadLockManagers
protected static java.util.Map<java.lang.Thread,ReadLockManager> getReadLockManagers()
Return the deferred lock manager hashtable (thread - DeferredLockManager).
-
toString
public java.lang.String toString()
Print the nested depth.- Overrides:
toStringin classjava.lang.Object
-
getStack
public java.lang.Exception getStack()
-
setStack
public void setStack(java.lang.Exception stack)
-
shouldTrackStack
public static boolean shouldTrackStack()
-
setShouldTrackStack
public static void setShouldTrackStack(boolean shouldTrackStack)
INTERNAL: This can be set during debugging to record the stacktrace when a lock is acquired. Then once IdentityMapAccessor.printIdentityMapLocks() is called the stack call for each lock will be printed as well. Because locking issues are usually quite time sensitive setting this flag may inadvertently remove the deadlock because of the change in timings. There is also a system level property for this setting. "eclipselink.cache.record-stack-on-lock"- Parameters:
shouldTrackStack-
-
putThreadAsWaitingToAcquireLockForWriting
public void putThreadAsWaitingToAcquireLockForWriting(java.lang.Thread thread, java.lang.String methodName)Normally this mehtod should only be called from withing the concurrency manager. However the write lock manager while it is building clones also does some while loop waiting to try to acquire a cache key this acquiring logic is not being managed directly inside of the wait manager.
-
removeThreadNoLongerWaitingToAcquireLockForWriting
public void removeThreadNoLongerWaitingToAcquireLockForWriting(java.lang.Thread thread)
The thread has acquired the lock for writing or decided to defer acquiring the lock putting this lock into its deferred lock list.
-
putThreadAsWaitingToAcquireLockForReading
public void putThreadAsWaitingToAcquireLockForReading(java.lang.Thread currentThread, java.lang.String methodName)The thread is trying to acquire a read lock but it is not being able to make process on getting the read lock.- Parameters:
methodName- metadata to help us debug trace leaking. If we start blowing up threads we do not want the traces created by the current thread to remain.
-
removeThreadNoLongerWaitingToAcquireLockForReading
public void removeThreadNoLongerWaitingToAcquireLockForReading(java.lang.Thread thread)
-
getConcurrencyManagerId
public long getConcurrencyManagerId()
Getter forconcurrencyManagerId
-
getConcurrencyManagerCreationDate
public java.util.Date getConcurrencyManagerCreationDate()
Getter forconcurrencyManagerCreationDate
-
getTotalNumberOfKeysAcquiredForReading
public long getTotalNumberOfKeysAcquiredForReading()
Getter fortotalNumberOfKeysAcquiredForReading
-
getTotalNumberOfKeysReleasedForReading
public long getTotalNumberOfKeysReleasedForReading()
Getter fortotalNumberOfKeysReleasedForReading
-
getTotalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero
public long getTotalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero()
-
getThreadsToWaitOnAcquire
public static java.util.Map<java.lang.Thread,ConcurrencyManager> getThreadsToWaitOnAcquire()
Getter forTHREADS_TO_WAIT_ON_ACQUIRE
-
getThreadsToWaitOnAcquireMethodName
public static java.util.Map<java.lang.Thread,java.lang.String> getThreadsToWaitOnAcquireMethodName()
-
getThreadsToWaitOnAcquireReadLock
public static java.util.Map<java.lang.Thread,ConcurrencyManager> getThreadsToWaitOnAcquireReadLock()
Getter forTHREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK
-
getThreadsToWaitOnAcquireReadLockMethodName
public static java.util.Map<java.lang.Thread,java.lang.String> getThreadsToWaitOnAcquireReadLockMethodName()
-
getThreadsWaitingToReleaseDeferredLocks
public static java.util.Set<java.lang.Thread> getThreadsWaitingToReleaseDeferredLocks()
Getter forTHREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS
-
getThreadsWaitingToReleaseDeferredLocksJustification
public static java.util.Map<java.lang.Thread,java.lang.String> getThreadsWaitingToReleaseDeferredLocksJustification()
-
addReadLockToReadLockManager
protected void addReadLockToReadLockManager()
The current thread has incremented the number of readers on the current cache key. It also wants to record into the read lock manager that this thread has acquired the cache key. This method should be user in all places where the cache key nunber of readers is incremented.
-
removeReadLockFromReadLockManager
protected void removeReadLockFromReadLockManager()
The current thread is about to decrement the number of readers in cache key. The thread also wants to update the read lock manager and remove the cache key that has previously been aquired from there.
-
getReadLockManagerEnsureResultIsNotNull
protected static ReadLockManager getReadLockManagerEnsureResultIsNotNull(java.lang.Thread thread)
Same asgetReadLockManager(Thread)but in this case a not null result is ensured- Parameters:
thread- the thread wanting its read lock manager- Returns:
- the read lock manager for the current thread.
-
removeReadLockManagerIfEmpty
protected static void removeReadLockManagerIfEmpty(java.lang.Thread thread)
Just like we see that the satic map of deffered locks is cleared of cache values for the current thread we also want to try to keep the static map of acquired read locks by a thread light weight by removing the association between the current thread and a read lock manager whenever the read lock manager becomes empty.- Parameters:
thread- the thread that wants its read lock manager destroyed if it is empty.
-
clearJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse
public static void clearJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse()
Clear the justification why theisBuildObjectOnThreadComplete(Thread, Map, List, boolean)is going nowhere.WHEN TO INVOKE:
Should be invoked if we decide to blowup a thread with the explosive approach, for a thread in wait for release deferred lock. We do not want to keep traces of threads that left eclipselink code.
Should be infokved when the algorithm returns TRUE - build object is complete.
Should be invoked when we are not yet stuck for sufficient time and the release defferred logic algorithm is using theisBuildObjectOnThreadComplete(Thread, Map, List, boolean)instead of the more verbose and slowerisBuildObjectOnThreadComplete(Thread, Map, List, boolean).
-
setJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse
public static void setJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse(java.lang.String justification)
SeeclearJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse()in this case we want to store the justification computed by theenrichStringBuildingExplainWhyThreadIsStuckInIsBuildObjectOnThreadComplete(List, ConcurrencyManager, Thread, boolean, StringBuilder)- Parameters:
justification- a string that helps us understand why the recursive algorithm returned false, building object is not yet complete.
-
-