在java中,我的客户端有2个线程,一个是控制网络流,另一个是处理消息,绘制游戏等。我想做的是当数据包到来时,网络线程将调用 messageReceived 方法游戏线程,包含消息作为参数。如果我将函数 messageReceived 设置为同步并且在 messageReceived 函数结束之前依次有 2 个数据包,它会阻塞网络线程,或者它没有阻塞并且我的数据包丢失,因为网络线程无法调用已经被使用的 messageReceived 函数通过游戏线程?
6 回答
当您使用 synchronized 关键字同步代码部分时,当另一个线程进入想要访问该部分时,它将阻塞,直到它可以访问。
正确,您正在阻塞 IO 线程。您只想在 messageReceived() 上做一些简单的工作,因为……也许只在某种 FIFO 中排队消息,以便处理线程稍后处理。您的同步块应该尽可能小。
如果一个线程调用一个类中的同步方法,所有其他线程将被阻止调用该类中的任何同步方法,因为对象锁不可用。如果您的 messageReceived 不适用于任何共享资源,请使其保持非同步。如果它正在使用一些共享资源,则尝试通过将同步代码包装在同步块中来最小化同步代码。
如果您使用更主流的设计模式(例如观察者模式),听起来您正在尝试解决一个可以轻松避免的问题。http://en.wikipedia.org/wiki/Observer_pattern
是的,synchronized
如果隐式锁已被另一个线程锁定,则阻塞一个线程。但是有一个非阻塞的替代方案 -java.util.concurrent.locks.Lock
更灵活
Lock.tryLock()
- 只有在调用时它是空闲的,才获取锁
和
Lock.tryLock(long time, TimeUnit unit)
- 如果在给定的等待时间内空闲并且当前线程没有被中断,则获取锁。
它很容易概念化,但我更像是一个视觉化的人。这里有一些代码很久以前就帮助我理解了 excatly syncorized 做了什么以及它是如何工作的。如果您观察输出,您将在将同步属性添加到打印功能时看到,您永远不会看到 As 和 Bs 混合。但是当你删除它时,你会看到一个非常不同的输出。一旦你看到它,它应该是直截了当的。
public class Main {
public static void main(String[] args) {
(new ThreadA()).start();
(new ThreadB()).start();
}
// try it with removing the synchronized: public static void print(String str) {
public static synchronized void print(String str) {
for(int i = 0; i<100; i++)
System.out.print(str);
System.out.println();
}
public static class ThreadA extends Thread {
public void run() {
while(true) {
print("A");
}
}
}
public static class ThreadB extends Thread {
public void run() {
while(true) {
print("B");
}
}
}
}