【发布时间】:2014-07-17 23:14:56
【问题描述】:
我正在为何时读取通道以及何时在 Netty 中写入通道创建侦听器。监听器是在 Bukkit 的事件系统中处理的,这不是问题,问题是线程安全。 Bukkit Api 在主线程上运行,必须同步,否则基本上服务器会爆炸。 Netty 在多个线程上运行,因此使用 Bukkit api 进行交叉通信可能会很困难。我在 Bukkit 论坛上问过,我得到的最佳答案是创建一个 AtomicBoolean 并有一个 while 循环,这将解决同步取消发送和写入数据包的问题,但它不能解决更改数据包的问题正在发送/写入。当在 Bukkit 中调用事件时,会从 @EventHandler 下的每个类调用侦听器。所有这些代码都必须与主线程同步,我不确定在这种情况下我将如何使用同步。抱歉代码格式不好,我不能很好地工作这个系统。
public class ConnectionInjector extends ChannelDuplexHandler {
private User user;
private Channel channel;
private PacketRecieveEvent recieve;
private PacketSendEvent send;
private boolean isInjected = false;
private boolean isClosed = false;
public ConnectionInjector(User user) {
this.init(user.getPlayer());
}
public void close() {
if (!this.isClosed) {
this.isClosed = true;
if (this.isInjected) {
getChannel().eventLoop().submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
getChannel().pipeline().remove(ConnectionInjector.this);
return null;
}
});
this.isInjected = false;
}
}
}
public boolean isInjected() {
return this.isInjected;
}
public boolean isClosed() {
return this.isClosed;
}
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channel) {
if (this.isClosed()){
throw new IllegalStateException("Connection closed already");
}
PacketData d = new PacketData(packet);
send = new PacketSendEvent(user, d);//the event
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
public void run() {
Bukkit.getPluginManager().callEvent(send); //runnable puts it on the main thread
}
});//end of bukkit thread back on netty
if (!send.isCancelled()){// this possible happens before the above code which is very bad
try {
if (send.getPacket().getRawPacket() == null){
throw new IllegalStateException("sent packet was null: " + send.getPacket().getRawPacket().getClass().getName());
}
super.write(context, send.getPacket().getRawPacket(), channel); //possible happens before send is even created.
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void channelRead(ChannelHandlerContext context, final Object packet) throws Exception {
PacketData p = new PacketData(packet);
recieve = new PacketRecieveEvent(user, p);
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
public void run() {
Bukkit.getPluginManager().callEvent(recieve);
}
});
if (recieve.isCancelled()){
super.channelRead(context, recieve.getPacket().getRawPacket());
}
}
public void injectfakePacket(Object packet) {
if (this.isClosed()){
throw new IllegalStateException("Injector is closed");
}
this.getChannel().pipeline().context("encoder").fireChannelRead(packet);
}
public void init(Player player) {
this.channel = (Channel) Nms.getNetChannel(player);
this.channel.pipeline().addBefore("packet_handler", "epickitpvp", this);
this.isInjected = true;
}
public Channel getChannel() {
if (this.channel == null){
throw new IllegalStateException("channel is null");
}
return this.channel;
}
}
【问题讨论】:
-
如果不需要,也不需要使用同步。您的字段只需要最终确定或变为 volatile,因为非原始 nonfloat/nondouble 字段主要不写入,除非在构造函数中,写入的字段是原始字段,只需要您设置为 volatile。顺便说一句,又是我:P
标签: java multithreading synchronization netty bukkit