Правильная синхронизация с помощью Lock в Java
Доброго времени суток всем. Помогите, пожалуйста, советом. Нужно решить задачу. У меня есть класс с main методом:
public class ThreadExample {
public static void main(String [] args) {
Foo foo = new Foo();
CompletableFuture.runAsync(() -> {
foo.first(new Thread());
});
CompletableFuture.runAsync(() -> {
foo.second(new Thread());
});
CompletableFuture.runAsync(() -> {
foo.third(new Thread());
});
}
}
и есть класс:
public class Foo {
Lock lock = new ReentrantLock();
public void first(Runnable r) {
lock.lock();
System.out.print("first");
lock.unlock();
}
public void second(Runnable r) {
lock.lock();
System.out.print("second");
lock.unlock();
}
public void third(Runnable r) {
lock.lock();
System.out.print("third");
lock.unlock();
}
}
Мне нужно, чтобы методы класса Foo вызывались синхронизированно. Так как я только новичок, у меня есть 100% подозрение, что я делаю что-то не так. Подскажите, пожалуйста, как правильно сделать так, чтобы если, допустим в одном методе класса Foo написать Thread.sleep(); другие методы дожидались его.
Ответы (1 шт):
Интерфейс Lock содержит метод newCondition(), который возвращает объект интерфейса Condition реализующий высокоуровневые аналоги методов wait/notify/notifyAll класса Object. Как было отмечено в комментарии, с помощью этих методов можно реализовать ожидание и уведомление потоков для организации необходимого порядка исполнения методов.
class Foo {
private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();
private int stage = 1;
public void first(Runnable r) {
lock.lock();
try {
System.out.print("first");
stage++;
cond.signalAll();
} finally {
lock.unlock();
}
}
public void second(Runnable r) {
lock.lock();
try {
while (stage != 2) cond.await();
System.out.print("second");
stage++;
cond.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void third(Runnable r) {
lock.lock();
try {
while (stage != 3) cond.await();
System.out.print("third");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Хотел бы отметить ещё один нюанс. В данной реализации для нотификации потоков используется метод signalAll(), который пробуждает все потоки находящиеся в состоянии wait. В выше описанном случае это не критично, но если потоков достаточно много, то это приведёт к избыточному пробуждению всех потоков, в то время как нужен только один. Эта проблема тоже решается разными способами, например созданием объекта Condition для каждого потока и усложнением логики нотификации с помощью метода signal().