У меня есть клиент-серверная архитектура, в которой серверы работают по очереди, передавая параметр каждый раз, когда клиент должен работать. Клиент работает с параметром, и после этого параметр становится «недействительным» и больше не может использоваться для выполнения работы.
Я бы хотел избежать запуска сборщика мусора, пока это происходит, и поэтому я избегаю выделения объектов. Таким образом, план состоит в том, что сервер будет ассоциировать один объект параметра с каждым клиентом и всегда будет передавать один и тот же параметр каждый раз, когда клиента просят выполнить работу. Однако это создает проблему, состоящую в том, что параметр должен быть переустановлен на «действительный», и в то же время гарантируется, что клиент (который, возможно, сохранил ссылку на параметр с прошлого раза) не может начать использовать его (скажем в другой ветке) прежде чем его попросят начать делать работу.
Таким образом, все открытые методы параметра синхронизируются, и устанавливается «действительное» состояние, за которым следует (синхронный) beginWork
вызов клиента внутри блока synchronized
. Но это создает проблему, заключающуюся в том, что клиент неосознанно удерживает блокировку параметра, что может вызвать проблемы, если клиент хочет разделить свою работу на несколько потоков. Поэтому я ввел однопоточный ExecutorService
, который сервер использует для отключения вызова на beginWork
, что гарантирует, что сервер быстро снимет блокировку. Но мне кажется, что это плохой дизайн - зачем этому классу нужен совсем другой поток?
Итак, мой вопрос: учитывая все, что я только что изложил, я допустил какую-то ужасную ошибку в дизайне, из-за которой я слишком усложнил этот класс, или это действительно должен быть такой комплекс?
interface Client {
public void doWork(Param p);
}
interface Param {
public boolean isValid();
}
class Server {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final MyParam[] params;
private class MyParam implements Param {
boolean isValid;
Client client;
Runnable task = new Runnable() {
@Override
public void run() {
client.doWork(MyParam.this);
}
}
@Override
public synchronized boolean isValid() {
return isValid;
}
}
public void runClients() {
while (true) {
for (MyParam param : params) {
synchronized(param) {
param.isValid = true;
// fork the client so we release the lock promptly (ugly!)
executor.execute(param.task);
}
// ... wait for the client to finish ...
}
}
}
}