Two ways to achieve thread safety in Java functions: pessimistic lock: acquire the lock before accessing data to prevent concurrent access by other threads to ensure data consistency. (synchronized keyword) Optimistic locking: Verify data at the end of the transaction and roll back the transaction if the data is modified to improve concurrency. (Atomic class in the java.util.concurrent.atomic package)
How do pessimistic locks and optimistic locks in Java functions achieve thread safety?
Thread safety is crucial for multi-threaded environments, ensuring the integrity and consistency of data when accessing data concurrently. In Java, pessimistic locking and optimistic locking are the two major mechanisms to achieve thread safety. Below we will explore how they are implemented and provide practical examples.
Pessimistic lock
Pessimistic lock is based on the assumption that data may be modified by other threads at any time. Therefore, it acquires the lock as soon as it accesses the data, preventing other threads from accessing the data until the lock is released. The advantage of pessimistic locking is that it can ensure data consistency, but the disadvantage is that it may lead to lock competition and deadlock.
synchronized
keyword is a common way to implement pessimistic locking in Java. It marks a block of code as a critical section, and only the thread that acquires the lock can enter the block of code.
public class Counter { private int count; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
Optimistic lock
Optimistic locking is based on the assumption that when a thread accesses data, the data is unlikely to be modified by other threads. It only validates the data at the end of the transaction and rolls back the transaction if the data has been modified. The advantage of optimistic locking is that it can improve concurrency, but the disadvantage is that if the data is modified, it may cause the transaction to fail.
In Java, the atomic class in the java.util.concurrent.atomic
package can implement optimistic locking. The operations in the atomic class are atomic, ensuring the correctness of concurrent access to data.
import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
Practical case: multi-threaded bank account
In order to demonstrate the application of pessimistic locking and optimistic locking in actual scenarios, we consider a multi-threaded bank account.
public class BankAccount { private int balance; public synchronized void withdraw(int amount) { if (balance >= amount) { balance -= amount; } } public synchronized int getBalance() { return balance; } }
import java.util.concurrent.atomic.AtomicInteger; public class BankAccount { private AtomicInteger balance = new AtomicInteger(0); public void withdraw(int amount) { while (true) { int currentBalance = balance.get(); if (currentBalance >= amount) { if (balance.compareAndSet(currentBalance, currentBalance - amount)) { break; } } else { break; } } } public int getBalance() { return balance.get(); } }
Using an optimistic lock, on a withdrawal, it gets the current balance and then attempts to atomically subtract the withdrawal amount using compareAndSet
. If the balance is insufficient, the operation will fail and the thread will retry.
Choose pessimistic lock or optimistic lock
Choose pessimistic lock or optimistic lock depends on the specific scenario. If concurrent access to the data is rare, or data consistency is critical, pessimistic locking is more appropriate. If concurrent access to data is frequent and data consistency allows a certain degree of compromise, optimistic locking is more appropriate.
The above is the detailed content of How do pessimistic locks and optimistic locks in Java functions achieve thread safety?. For more information, please follow other related articles on the PHP Chinese website!