Locks and Conditions
In concurrent programming, locks and conditions are advanced synchronization mechanisms that provide more flexibility and control over the execution flow of threads.
Locks
Locks are used to control access to shared resources by multiple threads. When a thread wants exclusive access to a shared resource, it acquires the lock associated with that resource. Other threads trying to acquire the same lock will be blocked until the lock is released.
Java provides the java.util.concurrent.locks.Lock
interface that defines the basic operations of a lock. One of the commonly used lock implementations is java.util.concurrent.locks.ReentrantLock
.
Here's an example of how to use a ReentrantLock
to control access to a shared resource:
1import java.util.concurrent.locks.Lock;
2import java.util.concurrent.locks.ReentrantLock;
3
4public class Main {
5
6 private Lock lock = new ReentrantLock();
7 private int count = 0;
8
9 public void increment() {
10 lock.lock();
11 try {
12 count++;
13 } finally {
14 lock.unlock();
15 }
16 }
17
18 public static void main(String[] args) {
19 Main main = new Main();
20
21 // Create multiple threads
22 for (int i = 0; i < 10; i++) {
23 Thread thread = new Thread(() -> {
24 for (int j = 0; j < 1000; j++) {
25 main.increment();
26 }
27 });
28 thread.start();
29 }
30
31 try {
32 Thread.sleep(2000);
33 } catch (InterruptedException e) {
34 e.printStackTrace();
35 }
36
37 System.out.println("Count: " + main.count);
38 }
39}
Conditions
Conditions are used to manage the execution flow of threads within a lock. They allow threads to wait until a specific condition is met and to signal other threads when the condition becomes true.
Java provides the java.util.concurrent.locks.Condition
interface for working with conditions. Conditions are created from a lock using the newCondition()
method.
Here's an example of how to use a condition to coordinate the execution of two threads:
1import java.util.concurrent.locks.Condition;
2import java.util.concurrent.locks.Lock;
3import java.util.concurrent.locks.ReentrantLock;
4
5public class Main {
6
7 private Lock lock = new ReentrantLock();
8 private Condition condition = lock.newCondition();
9 private boolean isDataReady = false;
10
11 public void producer() throws InterruptedException {
12 lock.lock();
13 try {
14 // Produce data
15 isDataReady = true;
16 // Signal the consumer
17 condition.signal();
18 } finally {
19 lock.unlock();
20 }
21 }
22
23 public void consumer() throws InterruptedException {
24 lock.lock();
25 try {
26 while (!isDataReady) {
27 // Wait for data to be ready
28 condition.await();
29 }
30 // Consume data
31 isDataReady = false;
32 } finally {
33 lock.unlock();
34 }
35 }
36
37 public static void main(String[] args) {
38 Main main = new Main();
39
40 // Create producer thread
41 Thread producerThread = new Thread(() -> {
42 try {
43 Thread.sleep(2000);
44 main.producer();
45 } catch (InterruptedException e) {
46 e.printStackTrace();
47 }
48 });
49
50 // Create consumer thread
51 Thread consumerThread = new Thread(() -> {
52 try {
53 main.consumer();
54 } catch (InterruptedException e) {
55 e.printStackTrace();
56 }
57 });
58
59 producerThread.start();
60 consumerThread.start();
61
62 try {
63 producerThread.join();
64 consumerThread.join();
65 } catch (InterruptedException e) {
66 e.printStackTrace();
67 }
68 }
69}
In this example, the producer
thread acquires the lock, produces some data, and signals the consumer
thread using the condition's signal()
method. The consumer
thread waits for the data to be ready by calling the condition's await()
method.
By using locks and conditions, you can implement advanced thread synchronization scenarios where precise control over the execution flow is required.
xxxxxxxxxx
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean isDataReady = false;
public void producer() throws InterruptedException {
lock.lock();
try {
// Produce data
isDataReady = true;
// Signal the consumer
condition.signal();
} finally {
lock.unlock();
}
}
public void consumer() throws InterruptedException {
lock.lock();
try {
while (!isDataReady) {
// Wait for data to be ready
condition.await();
}