Advanced Java Multithreading: from synchronized to Lock-Free Algorithms
synchronized is the earliest synchronization mechanism in Java. It is simple and easy to use and has good performance after optimization, but lacks flexibility; 2. ReentrantLock provides advanced functions such as interruptibility, reentrant, and support fairness, which is suitable for scenarios that require fine control; 3. The lock-free algorithm implements non-blocking concurrency based on CAS, such as AtomicLong, LongAdder and ConcurrentLinkedQueue, which performs better in a high-competitive environment, but needs to deal with ABA problems and CPU spin overhead; ultimately, appropriate strategies should be selected based on the concurrency strength: synchronized for low-competitive competition, and ReentrantLock for control, and lock-free structure for high-concurrency scenarios, so as to achieve the evolution from basic synchronization to high-performance concurrency.
Java's multi-threaded programming has gradually evolved from the early synchronized
keyword to the more flexible Lock
framework, and then to the lock-free (Lock-Free) algorithm widely used in high-performance scenarios today. Mastering this evolution path will not only help to write more efficient concurrent programs, but also provide a deep understanding of the concurrency mechanisms at the modern JVM and hardware levels.

The following is an analysis from three levels: synchronized
→ explicit lock ( ReentrantLock
) → lock-free algorithm (Lock-Free), which takes you from basic to advanced.
1. synchronized: the most primitive but still efficient synchronization mechanism
synchronized
is the earliest thread synchronization keyword provided by Java. It can be used in method or code blocks to ensure that only one thread can enter the critical area at the same time.

public synchronized void increment() { count ; }
advantage:
- Simple and easy to use, JVM automatically manages the acquisition and release of locks.
- After a lot of optimizations (biased locks, lightweight locks, lock expansion, etc.) after JDK 1.6, the performance has been greatly improved.
shortcoming:
- It is not interruptible, and the interrupt cannot be responded to when the thread is blocked.
- Cannot try to add lock (no tryLock).
- Fairness control is not supported.
- The locking particle size is relatively coarse and has poor flexibility.
Nevertheless, synchronized
is still preferred in most ordinary concurrency scenarios , because its performance is already very close to explicit locking and is not prone to errors.
2. Explicit lock: more flexible control
Java 5 introduces the java.util.concurrent.locks.Lock
interface, the most commonly used is ReentrantLock
, which provides richer functions than synchronized
.

private final ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count ; } finally { lock.unlock(); } }
Key Advantages:
- Support interruptible lock acquisition (
lockInterruptibly()
) - Supports trying to add locks (
tryLock()
) to avoid infinite waiting - Support fair locks (
true
is passed in during construction) to reduce thread hunger - Fine-grained conditional control (
Condition
)
Recommended usage:
- Use
ReentrantLock
when you need timeout attempts, interrupt responses, or fairness. - It must be used in conjunction with
try-finally
, otherwise deadlocks are prone to occur.
But note: explicit locks are still "blocking" . If the thread cannot get the lock, it will be suspended, and context switching will bring overhead.
3. Lock-Free algorithm: non-blocking concurrency based on CAS
True high-performance concurrency, especially in high-competitive scenarios (such as high-frequency trading, high-concurrency counters, lock-free queues), needs to get rid of the concept of "lock" and turn to the Lock-Free or Wait-Free algorithm.
Its core is CAS (Compare-And-Swap) operation, provided by Unsafe
class, encapsulated in Java through java.util.concurrent.atomic
package.
CAS Principle:
// Pseudocode: update to update only if the current value is equal to expect boolean compareAndSet(int expect, int update);
Example: Atomic integer counter
private final AtomicLong counter = new AtomicLong(0); public void increment() { counter.incrementAndGet(); // Internal based CAS loop}
ABA issues with CAS
CAS only compares whether the values are equal, and does not care whether the middle has been modified. For example:
- Thread 1 reads the value A
- Thread 2 Turn A → B → A
- Thread 1 executes CAS(A, new_value), successfully, but the intermediate state has been tampered with
Solution : Use AtomicStampedReference
to introduce a version number (timestamp) to distinguish real changes.
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0); int stamp = ref.getStamp(); Integer value = ref.getReference(); if (ref.compareAndSet(value, newValue, stamp, stamp 1)) { // Updated successfully}
4. Advanced Lock-Free Data Structure
JDK provides some high-performance containers based on lock-free algorithms:
1. ConcurrentLinkedQueue
- Lockless thread-safe queue
- Implement
offer()
andpoll()
based on CAS - Suitable for high concurrency producers-consumer scenarios
2. AtomicIntegerFieldUpdater
/ AtomicReferenceFieldUpdater
- Can perform atomic operations on the
volatile
field of a normal object - Avoid creating
AtomicInteger
objects for each field, saving memory
public class Task { volatile int state; static final AtomicIntegerFieldUpdater<Task> updater = AtomicIntegerFieldUpdater.newUpdater(Task.class, "state"); public boolean setStateIf(int expected, int newValue) { return updater.compareAndSet(this, expected, newValue); } }
3. LongAdder
vs AtomicLong
- In high concurrency accumulation scenarios,
LongAdder
performance is far superiorAtomicLong
- Principle: segmented accumulation (segment of segmented locking idea of ConcurrentHashMap), final summary
private final LongAdder adder = new LongAdder(); public void increment() { adder.increment(); // There are fewer conflicts under high concurrency} public long getTotal() { return adder.sum(); // Summarize the values of all segments}
5. Implement a simple Lock-Free stack (lockless stack)
public class LockFreeStack<T> { private final AtomicReference<Node<T>> top = new AtomicReference<>(); private static class Node<T> { final T value; final Node<T> next; Node(T value, Node<T> next) { this.value = value; this.next = next; } } public void push(T value) { Node<T> newHead = new Node<>(value, null); Node<T> currentHead; do { currentHead = top.get(); newHead = new Node<>(value, currentHead); } while (!top.compareAndSet(currentHead, newHead)); } public T pop() { Node<T> currentHead; Node<T> newHead; do { currentHead = top.get(); if (currentHead == null) return null; newHead = currentHead.next; } while (!top.compareAndSet(currentHead, newHead)); return currentHead.value; } }
This stack uses AtomicReference
and CAS to implement push/pop, and there is no lock in the whole process, but please note:
- Loop retry (spin) may consume CPU
- Not suitable for long-term operation
Summary: The evolution logic from synchronized to Lock-Free
characteristic | synchronized | ReentrantLock | Lock-Free |
---|---|---|---|
Ease of use | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
Performance (low competition) | high | high | Extremely high |
Performance (high competition) | Significant decline | Significant decline | Still efficient |
Interruptible | ❌ | ✅ | ✅(Non-blocking) |
Fairness | ❌ | ✅ | N/A |
ABA Questions | not applicable | not applicable | Need to be processed |
Recommendations for use
-
synchronized
is preferred for normal scenarios, which is fast and safe enough. - Use
ReentrantLock
when timeout, interrupt, and fairness are required. - High concurrency counting and status update use
AtomicXXX
orLongAdder
. - When building high-performance queues, stacks, etc., consider
ConcurrentLinkedQueue
or handwritten CAS structure. - Note: No lock ≠ no cost. CAS failed retry may cause CPU waste and need to be evaluated in combination with scenarios.
Basically that's it. By mastering these levels, you can choose the most appropriate synchronization strategy at different concurrency intensity, from "knowing to use locks" to "knowing concurrency".
The above is the detailed content of Advanced Java Multithreading: from synchronized to Lock-Free Algorithms. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

To correctly handle JDBC transactions, you must first turn off the automatic commit mode, then perform multiple operations, and finally commit or rollback according to the results; 1. Call conn.setAutoCommit(false) to start the transaction; 2. Execute multiple SQL operations, such as INSERT and UPDATE; 3. Call conn.commit() if all operations are successful, and call conn.rollback() if an exception occurs to ensure data consistency; at the same time, try-with-resources should be used to manage resources, properly handle exceptions and close connections to avoid connection leakage; in addition, it is recommended to use connection pools and set save points to achieve partial rollback, and keep transactions as short as possible to improve performance.

Virtual threads have significant performance advantages in highly concurrency and IO-intensive scenarios, but attention should be paid to the test methods and applicable scenarios. 1. Correct tests should simulate real business, especially IO blocking scenarios, and use tools such as JMH or Gatling to compare platform threads; 2. The throughput gap is obvious, and it can be several times to ten times higher than 100,000 concurrent requests, because it is lighter and efficient in scheduling; 3. During the test, it is necessary to avoid blindly pursuing high concurrency numbers, adapting to non-blocking IO models, and paying attention to monitoring indicators such as latency and GC; 4. In actual applications, it is suitable for web backend, asynchronous task processing and a large number of concurrent IO scenarios, while CPU-intensive tasks are still suitable for platform threads or ForkJoinPool.

TosetJAVA_HOMEonWindows,firstlocatetheJDKinstallationpath(e.g.,C:\ProgramFiles\Java\jdk-17),thencreateasystemenvironmentvariablenamedJAVA_HOMEwiththatpath.Next,updatethePATHvariablebyadding%JAVA\_HOME%\bin,andverifythesetupusingjava-versionandjavac-v

ServiceMesh is an inevitable choice for the evolution of Java microservice architecture, and its core lies in decoupling network logic and business code. 1. ServiceMesh handles load balancing, fuse, monitoring and other functions through Sidecar agents to focus on business; 2. Istio Envoy is suitable for medium and large projects, and Linkerd is lighter and suitable for small-scale trials; 3. Java microservices should close Feign, Ribbon and other components and hand them over to Istiod for discovery and communication; 4. Ensure automatic injection of Sidecar during deployment, pay attention to traffic rules configuration, protocol compatibility, and log tracking system construction, and adopt incremental migration and pre-control monitoring planning.

The key to implementing a linked list is to define node classes and implement basic operations. ①First create the Node class, including data and references to the next node; ② Then create the LinkedList class, implementing the insertion, deletion and printing functions; ③ Append method is used to add nodes at the tail; ④ printList method is used to output the content of the linked list; ⑤ deleteWithValue method is used to delete nodes with specified values and handle different situations of the head node and the intermediate node.

Preventing server-side template injection (SSTI) requires four aspects: 1. Use security configurations, such as disabling method calls and restricting class loading; 2. Avoid user input as template content, only variable replacement and strictly verify input; 3. Adopt sandbox environments, such as Pebble, Mustache or isolating rendering context; 4. Regularly update the dependent version and review the code logic to ensure that the template engine is configured reasonably and prevent the system from being attacked due to user-controllable templates.

To improve the performance of Java collection framework, we can optimize from the following four points: 1. Choose the appropriate type according to the scenario, such as frequent random access to ArrayList, quick search to HashSet, and concurrentHashMap for concurrent environments; 2. Set capacity and load factors reasonably during initialization to reduce capacity expansion overhead, but avoid memory waste; 3. Use immutable sets (such as List.of()) to improve security and performance, suitable for constant or read-only data; 4. Prevent memory leaks, and use weak references or professional cache libraries to manage long-term survival sets. These details significantly affect program stability and efficiency.

SetupaMaven/GradleprojectwithJAX-RSdependencieslikeJersey;2.CreateaRESTresourceusingannotationssuchas@Pathand@GET;3.ConfiguretheapplicationviaApplicationsubclassorweb.xml;4.AddJacksonforJSONbindingbyincludingjersey-media-json-jackson;5.DeploytoaJakar
