← Назад к вопросам

Как блокировать вызов на сервере

2.0 Middle🔥 81 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как блокировать вызов на сервере

Блокировка вызова на сервере — это остановка выполнения потока до наступления определённого события или условия. В Java есть несколько способов реализации, в зависимости от сценария.

1. Синхронизация через Lock и Condition

Это самый гибкий способ для блокировки потока в ожидании события:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class PaymentService {
    private Lock lock = new ReentrantLock();
    private Condition paymentProcessed = lock.newCondition();
    private boolean isPaid = false;
    
    // Поток ждёт платежа
    public void waitForPayment() throws InterruptedException {
        lock.lock();
        try {
            while (!isPaid) {
                System.out.println("Ожидаю платежа...");
                paymentProcessed.await(); // блокируем
            }
            System.out.println("Платёж получен!");
        } finally {
            lock.unlock();
        }
    }
    
    // Другой поток сигнализирует завершение
    public void processPayment() {
        lock.lock();
        try {
            isPaid = true;
            paymentProcessed.signalAll(); // разблокируем ожидающие потоки
            System.out.println("Платёж обработан");
        } finally {
            lock.unlock();
        }
    }
}

// Использование
PaymentService service = new PaymentService();

Thread waiter = new Thread(() -> {
    try {
        service.waitForPayment();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

Thread processor = new Thread(() -> {
    try {
        Thread.sleep(2000);
        service.processPayment();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

waiter.start();
processor.start();

2. CountDownLatch — блокировка до счётчика 0

Используется для блокировки одного или нескольких потоков до завершения операций:

import java.util.concurrent.CountDownLatch;

public class OrderService {
    public void processOrder() {
        int tasksCount = 3;
        CountDownLatch latch = new CountDownLatch(tasksCount);
        
        // Основной поток ждёт
        Thread mainThread = new Thread(() -> {
            try {
                System.out.println("Начинаю обработку заказа...");
                latch.await(); // блокируем до latch.countDown() 3 раза
                System.out.println("Все задачи выполнены!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // Фоновые потоки для отдельных задач
        for (int i = 1; i <= tasksCount; i++) {
            final int taskId = i;
            new Thread(() -> {
                try {
                    Thread.sleep(1000 * taskId);
                    System.out.println("Задача " + taskId + " выполнена");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    latch.countDown(); // уменьшаем счётчик
                }
            }).start();
        }
        
        mainThread.start();
    }
}

3. CyclicBarrier — блокировка до прихода всех потоков

Барьер, где потоки ждут друг друга:

import java.util.concurrent.CyclicBarrier;

public class RaceService {
    public void startRace() {
        int runners = 3;
        CyclicBarrier barrier = new CyclicBarrier(runners, () -> {
            System.out.println("=== СТАРТУЕМ! ===");
        });
        
        for (int i = 1; i <= runners; i++) {
            final int runner = i;
            new Thread(() -> {
                try {
                    System.out.println("Бегун " + runner + " на линии старта");
                    barrier.await(); // ждём остальных
                    System.out.println("Бегун " + runner + " стартует!");
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }
}

4. Semaphore — ограничение доступа

Блокировка до освобождения ресурса:

import java.util.concurrent.Semaphore;

public class DatabaseConnectionPool {
    private Semaphore semaphore;
    
    public DatabaseConnectionPool(int maxConnections) {
        this.semaphore = new Semaphore(maxConnections);
    }
    
    public void executeQuery(String query) throws InterruptedException {
        semaphore.acquire(); // ждём свободного слота
        try {
            System.out.println("Выполняю: " + query);
            Thread.sleep(2000); // имитация запроса
        } finally {
            System.out.println("Запрос завершён");
            semaphore.release(); // освобождаем слот
        }
    }
}

// Использование
DatabaseConnectionPool pool = new DatabaseConnectionPool(2);

for (int i = 1; i <= 5; i++) {
    final int id = i;
    new Thread(() -> {
        try {
            pool.executeQuery("SELECT * FROM users where id=" + id);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }).start();
}

5. Future/CompletableFuture — асинхронная блокировка

Для асинхронных операций:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ApiService {
    private ExecutorService executor = Executors.newFixedThreadPool(2);
    
    public void fetchData() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                return "Данные получены";
            } catch (InterruptedException e) {
                return "Ошибка";
            }
        }, executor);
        
        // Блокируем главный поток до результата
        String result = future.join(); // или future.get(5, TimeUnit.SECONDS)
        System.out.println(result);
    }
}

6. BlockingQueue — очередь с блокировкой

Для потокобезопасного обмена данными:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MessageService {
    private BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
    
    public void sendMessage(String msg) throws InterruptedException {
        queue.put(msg); // ждёт, если очередь полна
    }
    
    public String receiveMessage() throws InterruptedException {
        return queue.take(); // ждёт сообщения, если пусто
    }
    
    public void start() {
        // Потребитель
        new Thread(() -> {
            try {
                while (true) {
                    String msg = receiveMessage();
                    System.out.println("Получено: " + msg);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
        
        // Производитель
        new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    sendMessage("Сообщение " + i);
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

7. synchronized — базовая синхронизация

Простейший способ, но менее гибкий:

public class Account {
    private double balance = 1000;
    
    public synchronized void withdraw(double amount) {
        while (balance < amount) {
            try {
                wait(); // ждём депозита
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        balance -= amount;
        System.out.println("Снял: " + amount);
    }
    
    public synchronized void deposit(double amount) {
        balance += amount;
        notifyAll(); // уведомляем ждущие потоки
        System.out.println("Положил: " + amount);
    }
}

Выбор подходящего механизма

СитуацияМеханизм
Ждём событияLock + Condition
Ждём завершения N задачCountDownLatch
Синхронизация потоковCyclicBarrier
Пул ресурсовSemaphore
Асинхронные операцииCompletableFuture
Безопасный обменBlockingQueue
Простая синхронизацияsynchronized

Резюме

Блокировка вызова на сервере необходима для:

  • Синхронизации потоков
  • Ожидания внешних событий (БД, API, файлы)
  • Управления ресурсами
  • Обеспечения потокобезопасности

Выбор механизма зависит от конкретного сценария и требований к производительности.