Ответ 1
С картой Netty, Promise и Future являются объектами с однократной записью, этот принцип упрощает их использование в многопоточной среде.
Поскольку Promise не делает то, что вы хотите, нам нужно выяснить, подходят ли другие технологии для ваших условий, ваши условия в основном сводятся к следующему:
- Чтение из нескольких потоков
- Запись только из одного потока (как внутри канала Netty, метод чтения может выполняться только одним потоком одновременно, если канал не отмечен как общий)
Для этих требований наилучшее соответствие соответствия - это изменчивая переменная, так как она безопасна для чтения для чтения и может безопасно обновляться одним потоком, не беспокоясь о порядке записи.
Чтобы обновить свой код для использования с изменчивой переменной, он требует некоторых изменений, поскольку мы не можем легко передать ссылку ссылки на переменную внутри вашей функции, но мы должны передать функцию, которая обновляет базовую переменную.
private static volatile int connectedClients = 0;
public static void callBack () throws Exception{
//....
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {connectedClients = i;});
//....
}
public static void main(String[] args) throws Exception {
callBack();
while (true) {
System.out.println("The number if the connected clients is not two");
int ret = connectedClients;
if (ret == 2){
break;
}
}
System.out.println("The number if the connected clients is two");
}
public class ClientHandler extends ChannelInboundHandlerAdapter {
public final IntConsumer update;
public ClientHandler(IntConsumer update) {
this.update = update;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
RequestData msg = new RequestData();
msg.setIntValue(123);
msg.setStringValue("all work and no play makes jack a dull boy");
ctx.writeAndFlush(msg);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
update.accept(Integer.parseInt(msg));
}
}
В то время как вышеприведенный подход должен работать, мы быстро видим, что цикл while внутри основного класса использует большую долю времени процессора, и это может повлиять на другие части вашей локальной клиентской системы, к счастью, эта проблема также разрешима, если мы добавьте другие компоненты в систему, а именно синхронизацию. Если оставить начальное чтение connectedClients
вне блока синхронизации, мы все равно можем извлечь выгоду из быстрого чтения в случае "истинного" случая, а в случае "ложного" случая мы можем безопасно использовать важные циклы ЦП, которые может использоваться в других частях вашей системы.
Чтобы решить эту проблему, мы используем следующие шаги при чтении:
- Сохраните значение
connectedClients
в отдельной переменной - Сравните эту переменную с целевым значением
- Если это правда, тогда выйдите из цикла
- Если false, перейдите в синхронизированный блок
- запустить цикл while
- Снова прочтите переменную, так как теперь значение может быть изменено.
- Проверить состояние и разорвать, если условие сейчас правильно.
- Если нет, дождитесь изменения значения
И при написании:
- Синхронизировать
- Обновить значение
- Пробудите все остальные потоки, ожидающие этого значения.
Это может быть реализовано в коде следующим образом:
private static volatile int connectedClients = 0;
private static final Object lock = new Object();
public static void callBack () throws Exception{
//....
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
//....
}
public static void main(String[] args) throws Exception {
callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
}
Изменения на стороне сервера
Однако не все ваши проблемы связаны с клиентской стороной.
Если вы разместили ссылку на свой репозиторий github, вы никогда не отправляете запрос с сервера обратно старым клиентам, когда новый человек присоединился. Поскольку это не сделано, клиент никогда не уведомляется об изменениях, обязательно сделайте это.