When we use Java for network programming concepts often encounter a lot of overtime, such as a browser request process could produce a lot of overtime, when we are in a browser by request, network socket, speaking, reading and writing may timeout, web server response may timeout, database query may timeout. For Java concurrency, timeout is mainly related to thread wait timeout and lock timeout. For example, calling Object.wait(long) will cause the thread to enter the wait state and wait for timeout after a specified time.

This article focuses on the timeout mechanism of Java built-in lock acquisition operations. A timeout mechanism is needed when a large number of threads are competing for a lock, which may result in some threads being unable to acquire the lock for a long period of time. In some scenarios, it may be desirable to cancel the wait for the lock if the thread fails to acquire the lock for a period of time to improve performance.

Synchronized does not support timeout

Java provides concurrent locking from the syntactic level, starting with the synchronized keyword, which is familiar to us as Java’s built-in locking scheme. In the Java world, each object is associated with a built-in lock, and any thread accessing a synchronized modified object must acquire its lock before continuing to access it, or it will wait until the lock is released by another thread. Both ordinary objects and object methods are associated with built-in locks, so they can be modified by synchronized.

Synchronized, while convenient to use, has the disadvantage of not supporting a timeout mechanism for lock acquisition operations. In the case of concurrency, multiple threads will compete for the corresponding lock object modified by synchronized, and there may be a thread that has not been able to obtain the lock and has been in the blocking waiting state. The only thing the blocked thread can do is wait, and there is no way to set a timeout. For example, the thread acquires the lock and goes to sleep for a long time after “Thread1 gets the lock”. Thread two starts late and tries to acquire the lock, but the lock is already held by thread one, so thread one will never acquire the lock and will wait.

AQS synchronizer timeout mechanism

JUC tools were not available prior to JDK1.5, when concurrency control was limited to the aforementioned synchronized keyword, but it had limited control over timeout cancellation. The JUC tool introduced in JDK1.5 addresses this problem perfectly, mainly because the AQS synchronizer provides lock acquisition timeout support. We know that AQS synchronizer uses queue structure to process waiting threads, and the timeout mechanism of AQS lock acquisition is roughly shown in the figure below. First, multiple threads compete for the lock, and since the lock is already held by other threads, each thread is added to the end of the queue through a spin CAS operation. Second, after a thread is added to the queue, each thread node polls the previous node to see if it is its turn to acquire the lock. If thread 2 sets the timeout mechanism, and thread 2 does not acquire the lock within the timeout period, the node corresponding to this thread will be canceled. Finally thread 2 is cancelled due to a lock timeout.

Timeout implementation logic

To be more precise about the accuracy of the time interval, the implementation uses the more precise system.nanotime () method, which is accurate to the nanosecond level. In general, the idea of timeout mechanism is to calculate the deadline time first, and then calculate whether the deadline time is reached in the continuous lock checking operation. If the deadline time is reached, cancel the node in the queue and break out of the loop.

AQS timeout control has two points that must be noted:

  • Firstly, the timeout time includes the time for competing to join the team. If the time for competing to join the team is exhausted, it will be treated as timeout directly.

  • The other is about the spinForTimeoutThreshold variable threshold, which is the dividing line that determines whether time is consumed using spin or system blocking.

The JUC toolkit author tested to set the default value to 1000ns, that is, if the remaining time after successfully inserting the wait queue is greater than 1000ns, the system underlying blocking is called. Otherwise, you don’t invoke system-level blocking, but instead just let it cycle through the Java layer for time, which is a performance optimization measure.

conclusion

Java’s built-in synchronized keyword provides concurrent locking, but it has the disadvantage of not supporting timeout. The AQS synchronizer provides timeout mechanism in the process of acquiring locks, and we deeply analyze the specific implementation principle of AQS obtaining lock timeout. Getting lock timeout support allows Java to provide a more sophisticated mechanism for concurrency, allowing developers to meet more of their concurrency strategy needs.