Урок 26: Высокоуровневые примитивы синхронизации
Высокоуровневые примитивы синхронизации в Java, такие как ReentrantLock
, Semaphore
и CountDownLatch
, предоставляют дополнительные возможности для управления потоками и синхронизации. Эти примитивы позволяют более гибко и эффективно решать задачи синхронизации по сравнению с традиционными средствами, такими как synchronized
.
ReentrantLock
ReentrantLock
— это замок, который предоставляет все возможности synchronized
, но с дополнительными функциями, такими как возможность попытки захвата замка, возможность прерывания захвата и возможность тайм-аутов.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + example.getCount());
}
}
Результат выполнения
Semaphore
Semaphore
ограничивает количество потоков, которые могут одновременно получить доступ к ресурсу. Это полезно для управления доступом к ресурсам, которые имеют ограниченное количество экземпляров, например, подключения к базе данных.
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(1);
public void accessResource() {
try {
semaphore.acquire();
System.out.println("Доступ к ресурсу получен");
Thread.sleep(1000); // Имитация работы с ресурсом
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("Ресурс освобожден");
}
}
public static void main(String[] args) {
SemaphoreExample example = new SemaphoreExample();
Thread t1 = new Thread(example::accessResource);
Thread t2 = new Thread(example::accessResource);
t1.start();
t2.start();
}
}
Результат выполнения
Доступ к ресурсу получен
Ресурс освобожден
Доступ к ресурсу получен
Ресурс освобожден
Упражнения
Упражнение 1: Использование ReentrantLock
Напишите программу, которая создает счетчик и два потока, увеличивающие значение счетчика с использованием ReentrantLock
для синхронизации доступа.
Решение:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + counter.getCount());
}
}
Объяснение: Программа создает объект Counter
и два потока, которые одновременно увеличивают значение счетчика. Для синхронизации используется ReentrantLock
, чтобы предотвратить одновременное изменение значения.
Упражнение 2: Использование Semaphore
Напишите программу, которая ограничивает доступ к ресурсу тремя потоками одновременно, используя Semaphore
.
Решение:
import java.util.concurrent.Semaphore;
public class ResourceAccess {
private final Semaphore semaphore = new Semaphore(3);
public void accessResource() {
try {
semaphore.acquire();
System.out.println("Доступ к ресурсу получен " + Thread.currentThread().getName());
Thread.sleep(1000); // Имитация работы с ресурсом
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("Ресурс освобожден " + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ResourceAccess resourceAccess = new ResourceAccess();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(resourceAccess::accessResource, "Поток-" + i);
thread.start();
}
}
}
Объяснение: Программа создает объект ResourceAccess
и пять потоков, которые пытаются получить доступ к ресурсу. Semaphore
позволяет одновременно получить доступ только трем потокам.