All sorts of questions can arise when people try to use your classes in concurrent programs. While it is not usually possible or desirable to document all of them for every class or method (especially for interfaces that cover multiple implementations, or library classes which may evolve), keep these in mind when writing documentation for others using, reviewing, co-developing, or maintaining your code.
Each property is given a name, just for ease of referencing. Some of the names are not very good. This is not an exhaustive categorization. Any given method or class will usually have several of these properties. Also, many of them can overlap. While these properties focus on properties of methods, many of them cannot be applied on a method-by-method basis, but instead be described together for all of the methods in a particular class, or even groups of related classes.
Usage. This usually holds for either all or none of the methods in a class. In-between cases are not common because it is difficult to promise that all invariants and postconditions hold across concurrent invocations of arbitrary sets of methods unless all are safe. Typically applies when methods rely only on immutable state, and/or locks or atomics controlling updates and accesses to mutable state. Additionally, the class cannot rely on any other independently accessible objects (including statics) in unsafe ways. All fields are private. All objects referenced by internal fields are either completely inaccessible outside this class or managed safely. For example, none of these helper classes have methods that may send this as an argument or return value to unmanaged code. In general, the safety of one method relies on the safety of all other objects and methods that it may access.
In practice, this and most other safety guarantees apply only if the caller also ensures that the reference to the object used for the call is itself maintained safely.
Usage. This requires that constructors or factory methods are Safe, and additionally do not make references to the constructed objects available for access by other threads (e.g., by recording in a static field) before initialization is complete.
Usage. Usually holds for either all or none of the methods in a class. Classes are Lockable in a useful sense if they do not access independently accessible mutable objects or their fields (including statics), or if they do, the set of such classes is small and all are also Lockable. A Lockable class may contain methods that don't actually require locking, for example Stateless methods that can run concurrently with others without need for any lock.
Usage. Typically holds when a class is Lockable, and additionally, all accessor methods are purely read-only. To be useful, you must indicate which methods require which kinds of locks (possibly along with indications of those methods that do not need either kind of lock):
Usage. Usually holds for either all or none of the methods in a class. Typically, classes are OwnerSafe if any independently accessible mutable objects or their fields (including statics) that they rely on are also at least OwnerSafe. Note that Lockable and OwnerSafe may be the same if the class does not access any such objects, but may still differ if some parts of a program adopt one or the other usage style in a way that other parts must also obey. A class is usefully OwnerSafe if the list of accessed classes is small and all are also OwnerSafe. The most common means of transferring ownership of an object across threads is via a BlockingQueue or similar structure in which a producer thread never again accesses an object or any OwnerSafe objects it relies on once it is placed in the queue.
Usage. Usually, this is a restricted form of OwnerSafe, but may also apply when a method relies on particular security settings (e.g., an assumed AccessControlContext). This property may preclude a task from being run in a thread pool unless each required ThreadLocal value can be listed and so can be set by worker threads before execution.
Usage. One common case is an object that undergoes a series of mutations by its owner, and is then never again modified, but may be safely read by other threads that never again change its state. For example a collection that is filled in, and then used only via Collections.unmodifiableCollection wrappers.
Usage. This may be the consequence of applying locks to a Lockable class. It may also hold for objects that are designed to be used only as components of some container class.
Usage. Not being FailureSafe with respect to unrecoverable failures (such as OutOfMemoryErrors) might be an exception to main safety properties of a class or method.
Usage. Usually, atomicity is ensured by using locks, or in designs based on atomics, by guaranteeing that there exists a "linearization point" before which an update is not in effect, and after which it is. Sometimes, a class will provide "core" methods that are atomic, plus other "layered" composite methods that are not necessarily atomic. For example, add vs addAll in a Collection. Providing callers with a way to guarantee atomicity of composite methods usually requires exposing internal synchronization mechanics of a class. Atomicity normally requires that all fields bearing some invariant relation to each other are only updated and accessed under a common lock, or represented using some kind of snapshot technique. All code must additionally that if a condition is assumed to hold that it actually does throughout the course of code relying on this, usually by placing the condition and action occur within the same locked code segment.
Usage. This applies to Thread.start, Executor.execute, AWT, Swing, and JavaBeans event triggers, and other methods that use them. Asynchronous methods may be accompanied by TerminationBlocking methods allowing callers to optionally wait out effects, as in Thread.Join, Futures, and invokeAndWait.
Usage. This normally applies to a Safe method whose actions and results rely only on its arguments and/or values of final fields (including final fields of other objects accessed by final references). A class containing only Stateless methods is Immutable.
Usage. An accessor method is AccessorConsistent if it cannot encounter read/write conflicts that might reveal state inconsistencies, or reorderings that might cause apparent lags with respect to concurrent updates. Generally, this can be ensured by using locks, or for aspects of state that need not obey invariants with others, accessing volatile or atomic fields. If an accessor can be invoked as a callback, it must be prepared for the case where internal state is not consistent because it is being called indirectly from the insides of another method during which fields have taken on transient values.
Usage. Usually, these decisions must be communicated with users of the view class only if it is possible for a view method to reveal a state of the base object inconsistent with that reported by the base object itself, especially across sequences of operations on the view object. Options range from simple locking to complex caching protocols. Common categories include:
Usage. This usually holds only for non-looping lockless code, but also applies to fancier nonblocking data structure operations that have somehow been proven to complete in a finite number of steps regardless of contention.
Usage. This is weakening of WaitFree, where the number of looping steps cannot be bounded beyond saying that they eventually terminate, generally because at least one of a contending set of threads will make progress in a finite number of steps.
Usage. This is a weakening of LockFree in which progress cannot be strictly guaranteed in the face of unbounded thread contention, and applies to methods that may require some kind of contention-avoidance (for example exponential backoff) to guarantee progress.
Usage. This is a weakening of ObstructionFree applying when a method may use locks, and so one thread may fail to make progress because another thread is suspended while holding a needed lock, and there might not be anything you can do to force progress beyond waiting for the OS/Scheduler to eventually cause the suspended thread to resume.
Usage.This is an alternative to BoundedLocking in which loops etc may occur, but unbounded delays of any given thread due to contention cannot occur, so long as the underlying OS/Scheduler maintains minimal fairness properties. Generally, a method claiming to be Fair can only call other Fair or WaitFree methods.
Usage. This may apply if it is possible for one thread to repeatedly acquire and release a lock without giving any other thread a chance to acquire it. One common case of this is methods with code of form for (...) synchronized(...) { ... } }, which might, without fairness guarantees, be executed as if it instead placed the synchronized around the loop. Usually, there are alternatives to such constructions.
Usage. This holds for any method that may encounter lock (including those in downstream calls) for which you cannot make any stronger liveness claims beyond the fact that it is not possible for an invocation of this method to be holding one lock while trying another, while another thread is doing the opposite. Note that a method might not itself be fully safe, yet still somehow encounter locks in the course of execution.
Usage. This is the most important property for users to know to prevent deadlocks, but often the hardest to accurately specify when a method relies on the locking properties of unknown downstream virtual calls.
Usage. This is most useful when applied to all accessor methods of a class.
Usage. Normally, balking applies when conditions are strictly state-based versus those that may somehow be related to values of arguments to the method.
Usage. It is normal practice to implicitly identify methods that may block on conditions by declaring that they can throw InterruptedException. implementations must ensure that it is not possible for a thread requiring a condition to block waiting for it even though another thread has already been signalled that the condition holds.
Usage. This is a special case of ConditionBlocking, where the condition being waited for is termiation of another task. It usually applies when a method blocks on Thread.join, Future.get, or related methods.
Usage. This applies to ConditionBlocking methods that block on resource availability. Typical means for achieving liveness under saturation include aborting (usually throwing an exception), shedding work, or applying back-pressure causing other threads producing work to slow down.
Usage. You may need to distinguish whether the method will report failure if the timeout elapses even if the waited-for condition does hold when the time elapses, or whether it will treat this as a successful case and proceed.
Usage. Condition polling is usually not a desirable feature, but instead a design compromise that users of a class have to tolerate. Usually, it is best to have a non-spinning ConditionBalking method so that that callers can control spin rates etc.
Usage. This is usually a consideration only for methods containing long or unbounded loops, as well as condition or IO blocks.
Usage. These are the most common concurrency-related Quality of Service considerations not otherwise deal with here.
Usage. There are many further sub-questions about IO and remote operations that frequently interact with concurrency but not dealt with here.