java - RMI 和同步

标签 java synchronization rmi

我目前正在用 Java 编写一个简单的聊天应用程序。它有一个服务器应用程序和一个客户端应用程序,均用 Java 编写并使用 RMI 进行通信。

在服务器上,有一个活跃用户的 ArrayList,当用户上线和离线时,该列表会更新。每个客户端到服务器的连接都有它自己的线程。

当建立新连接(即新客户端加入)时,将创建一个新线程,并在该线程内更新ArrayList以反射(reflect)新用户。同样,当连接丢失时,ArrayList 会更新以反射(reflect)断开连接。

显然,如果有很多客户端,就会有很多对 ArrayList 的并发访问。因此,我的问题是,是否应该同步访问/更新此 ArrayList 的任何方法?

最佳答案

其他答案建议使用 Collections.synchronizedList() .

List<User> list = Collections.synchronizedList(new ArrayList<>());

可能有效,但你必须非常小心。这样的列表仅对于直接在该列表上调用的各个方法来说是线程安全的。然而,许多操作涉及对列表的多个操作,例如迭代列表。例如,可以编写以下代码来向聊天中的每个用户发送消息:

for (User u : list)
    u.sendMessage(msg);

这将失败并显示 ConcurrentModificationException如果在迭代期间将用户添加到列表或从列表中删除。发生这种情况是因为这种形式的 for 循环得到 Iterator并通过 hasNext 重复访问该列表和next来电。可以在这些访问之间修改该列表。如果列表被修改,Iterator检测到这一点并抛出异常。

在 Java SE 8 中,可以改为编写以下内容:

list.forEach(u -> u.sendMesage(msg));

这是安全的,因为列表的锁定在 forEach 的整个持续时间内都保持着。打电话。

(另一个建议是使用 Set 而不是 List 。出于其他原因,这可能是一个好主意,但它遇到与 List 相同的并发问题。)

处理此问题的最佳方法可能是创建您自己的对象,该对象包含用户列表(或集)和任何相关数据,并在您的容器上定义一组特定的操作用户及相关数据。然后,在容器对象的方法周围添加适当的同步。这允许对除您的列表或用户组之外的其他数据进行线程安全更新。

最后,volatile如果在构造时、在对象对其他线程可见之前初始化列表字段,则没有必要。用 RMI 术语来说,如果在导出对象之前构造完成,那么您就是安全的。创建字段是一个很好的做法 final这样初始化后,初始化值对所有其他线程都是可见的,并且此后无法更改。

关于java - RMI 和同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21435829/

相关文章:

java - 如何使用 Java 对自定义日期反序列化器进行单元测试

java - 如何获取主文件夹的文件和子文件夹的更改?

java - Java RMI和高级多线程

java - 通过防火墙的 VisualVM - RMI 故障排除

java - MYSQL日期比较问题异常

java - 如何计算这些方法/算法的计算复杂度?

mysql - 不同主机上不同数据库之间的触发器

php - 如何同步mysql数据库从本地主机更新到在线服务器

java - 关于java RMI。如何使用LocateRegistry在指定端口的远程主机上获取Registry?

java - groovy 中的类加载问题