Java System.out.println()影响程序流程
Java System.out.println() affecting program flow
我正在使用 KryoNet java 库和 slick 开发基于 server/client 的游戏。当服务器 class 收到来自客户端的连接时,它会向客户端发送必要的启动信息,包括它是什么玩家号。收到此消息后,客户端开始流畅运行并开始正常运行。代码是:
boolean started = false;
while(!started){
System.out.println(cs.playerNum);
if(cs.playerNum != -1){
cs.startSlick();
started = true;
}
}
当从服务器接收到值时,playerNum 由另一个线程设置。有一段时间我无法让它工作(cs.startSlick() 从未被调用过),最终我感到沮丧并开始在每次循环 运行 时记录 playerNum。通过添加 System.out.println(cs.playerNum),代码开始工作,循环将正确评估并启动 slick。
System.out.println 怎么可能这样做?我尝试用其他函数替换它,甚至用 cs.playerNum 作为参数的其他函数替换它,但只有当我专门打印 cs.playerNum 时,我才能让循环工作。
如果我需要包含更多源代码,我可以,但问题似乎直接出在这里,因为我尝试用其他函数替换 System.out.println 但没有成功。
当你说 "The playerNum is set by another thread" 时,你算是回答了你自己的问题。你所拥有的是一个经典的竞争条件。如果您的代码能够足够快地执行,那么该 playerNum 将不会在需要时设置。但是,如果某些事情延迟或 "interrupt" 您的代码,那么其他线程将有时间设置 playerNum 值,并且您的代码将按预期工作。
执行 IO 的系统调用在等待 IO 操作发生时强行挂起线程。当您调用 System.out.println 时会发生这种情况,这会导致您看似紧凑的代码暂时暂停并屈服于另一个线程并允许您检索所需的值。
这是一个非常基本的线程问题,您将 运行 在编写线程代码时遇到更复杂的线程问题。因此,我绝对建议您花一些时间阅读有关线程的一般知识,并了解同步函数以及 wait() 和 notify() 的工作原理,正如评论中所建议的那样。
睡眠并不能解决问题表明这不仅仅是让线程有时间工作,而是关于跨线程的内存可见性。当您的线程调用 println 时,它会在控制台上获得一个锁,这会强制对 cs.playerNum 的更改变得可见。
您没有说明如何更新 playerNum 或它是否易变,但您似乎看到了一种优化,其中 JVM 不知道它需要让该线程知道 playerNum 的更新。 JVM 可以进行优化,例如重新排序字节码或缓存值,并且只有在您的代码表明不允许执行诸如使变量易变或进行锁定等操作时才知道不这样做。
应该避免忙等待,这确实需要替换为等待通知,或从队列中读取,或使用 java.util.concurrent 中的一些更高级别的构造。
我正在使用 KryoNet java 库和 slick 开发基于 server/client 的游戏。当服务器 class 收到来自客户端的连接时,它会向客户端发送必要的启动信息,包括它是什么玩家号。收到此消息后,客户端开始流畅运行并开始正常运行。代码是:
boolean started = false;
while(!started){
System.out.println(cs.playerNum);
if(cs.playerNum != -1){
cs.startSlick();
started = true;
}
}
当从服务器接收到值时,playerNum 由另一个线程设置。有一段时间我无法让它工作(cs.startSlick() 从未被调用过),最终我感到沮丧并开始在每次循环 运行 时记录 playerNum。通过添加 System.out.println(cs.playerNum),代码开始工作,循环将正确评估并启动 slick。
System.out.println 怎么可能这样做?我尝试用其他函数替换它,甚至用 cs.playerNum 作为参数的其他函数替换它,但只有当我专门打印 cs.playerNum 时,我才能让循环工作。 如果我需要包含更多源代码,我可以,但问题似乎直接出在这里,因为我尝试用其他函数替换 System.out.println 但没有成功。
当你说 "The playerNum is set by another thread" 时,你算是回答了你自己的问题。你所拥有的是一个经典的竞争条件。如果您的代码能够足够快地执行,那么该 playerNum 将不会在需要时设置。但是,如果某些事情延迟或 "interrupt" 您的代码,那么其他线程将有时间设置 playerNum 值,并且您的代码将按预期工作。
执行 IO 的系统调用在等待 IO 操作发生时强行挂起线程。当您调用 System.out.println 时会发生这种情况,这会导致您看似紧凑的代码暂时暂停并屈服于另一个线程并允许您检索所需的值。
这是一个非常基本的线程问题,您将 运行 在编写线程代码时遇到更复杂的线程问题。因此,我绝对建议您花一些时间阅读有关线程的一般知识,并了解同步函数以及 wait() 和 notify() 的工作原理,正如评论中所建议的那样。
睡眠并不能解决问题表明这不仅仅是让线程有时间工作,而是关于跨线程的内存可见性。当您的线程调用 println 时,它会在控制台上获得一个锁,这会强制对 cs.playerNum 的更改变得可见。
您没有说明如何更新 playerNum 或它是否易变,但您似乎看到了一种优化,其中 JVM 不知道它需要让该线程知道 playerNum 的更新。 JVM 可以进行优化,例如重新排序字节码或缓存值,并且只有在您的代码表明不允许执行诸如使变量易变或进行锁定等操作时才知道不这样做。
应该避免忙等待,这确实需要替换为等待通知,或从队列中读取,或使用 java.util.concurrent 中的一些更高级别的构造。