使用Netty和Bukkit进行线程安全

时间:2014-07-17 23:14:56

标签: java multithreading synchronization netty bukkit

我在读取频道时以及频道在Netty中写入时创建侦听器。听众是在Bukkit的事件系统中处理的,这不是问题,问题是线程安全。 Bukkit Api在主线程上运行,必须同步或基本上服务器将爆炸。 Netty在多个线程上运行,因此Bukkit api可能很难进行交叉通信。我在Bukkit论坛上问过,我得到的最好的答案是创建一个AtomicBoolean并有一个while循环,这可以解决同步取消发送和写入数据包的问题,​​但它没有解决改变的问题正在发送/写入的数据包。当在Bukkit中调用事件时,将从@EventHandler下的每个类调用侦听器。所有代码必须与主线程同步,我不确定在这种情况下我将如何使用synchronized。抱歉代码格式不好我不能很好地使用这个系统。

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;
    }
} 

2 个答案:

答案 0 :(得分:0)

您可以使用ASync线程访问Bukkit的Scheudler,因此您可以在ASync线程内部调整新的同步任务,因为只有在主线程就绪时才会调用同步线程。

复杂的短片:使用

伪装新的同步任务
Bukkit.getScheudler().scheudleSyncTask(PLUGIN, new Runnable() {
    public void run() {
        // Bukkit methods here
    }
});

在您的ASync线程中,你很好。

答案 1 :(得分:0)

我不明白您使用此代码完成了什么,所以我无法直接给您答案,但我可以解决您可能遇到的一些问题有线程。

首先,Java可以并且将基于每个线程缓存变量,这意味着您将根据您所处的线程以不同的值结束同一变量的多个副本。缓存可能会给那些不了解它的人带来很多困惑。但是,您可以通过使用volatile关键字告诉Java将变量保持在线程之间的一致状态。有关volatile的更多信息,请访问:http://www.javamex.com/tutorials/synchronization_volatile.shtml

如果您在理解Java中的synchronized关键字时遇到问题,我建议您首先对信号量是什么进行一些研究,然后重新访问同步。很好地理解信号量是什么以及它是如何工作的,这将使您更容易理解如何在线程需要共享状态的多线程环境中工作(但是,这是您应该尽可能避免的情况)。

您也可能会发现Java的并发集合api非常有用:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html