【问题标题】:Why isn't this while condition reevaluated?为什么不重新评估条件?
【发布时间】:2012-06-12 13:01:23
【问题描述】:

我有两个 while 条件,一个在另一个里面,用布尔值来控制它们。基本上,一个用于终止共享,另一个用于侦听连接。用户可以选择禁用共享,在这种情况下,服务器会停止侦听,但不会终止。如果用户选择终止,则两个布尔值都设置为 false 并且循环结束。

这是我的代码:

        public void run() {
            while (!terminate) {  
                while (listening) {
                    try {
                        // accept connection -> create a new thread for each client
                        ClientServerShareInstance clientServerShareInstance = new ClientServerShareInstance(serverSocket.accept(), ui);
                        Thread clientServerThread = new Thread(clientServerShareInstance);
                        clientSockets.add(clientServerShareInstance);
                        connectedClients++;
                        clientServerThread.start();
                    } catch (IOException ex) {
                    }
                }
            }
        }

        public void closeAllClientConnections() {
            for (Iterator it = clientSockets.iterator(); it.hasNext();) {
                ClientServerShareInstance clientServerShareInstance = (ClientServerShareInstance) it.next();
                clientServerShareInstance.closeAllConnections();
                it.remove();
            }
            try {
                this.serverSocket.close();
            } catch (IOException ex) {}
            this.setActive(false);
            this.connectedClients = 0;
        }


        public void openConnection() {
            try {
                serverSocket = new ServerSocket(portNumber, 0, Inet4Address.getLocalHost());
                setActive(true);
            } catch (IOException ex) {}
        }
    }

closeAllClientConnections() 方法禁用共享(不会终止它),openConnection() 重新启用该共享。

问题是,如果我禁用共享,它应该只是无限期地循环terminate while cicle,测试listening 的值。当我将listening设置为true时,它应该重新进入第二个while循环并再次开始监听,因为我确实打开了服务器套接字(虽然它与此无关,但我只是说它必须被初始化再次因为我在禁用共享时关闭它)。但是,禁用后,它永远不会租用 listening 循环,即使在调用 openConnection() 时也是如此。

有人知道这里有什么问题吗?

【问题讨论】:

  • setActive(...) 设置listening的值?
  • 不,它们只是在我的班级中被声明为常规私有布尔值。我没有使用 volatile,因为我认为对象是在多个线程之间共享的,并且我不需要锁,因为它不是一个复杂的操作,多个线程同时“争夺”访问权限。
  • @Gray 是的,确实如此。它只是该变量的设置器。
  • 我已将我的 cmets 移至答案。
  • 如何正确格式化您的代码...

标签: java multithreading sockets loops while-loop


【解决方案1】:

显示的代码不足,无法显示任何错误。但这里有一些可能会有所帮助的 cmets。

  • shutdownlistening 布尔值都必须是 volatile。线程之间共享的任何字段都需要以某种方式同步,否则其他线程将看不到对其值的更改。

  • serverSocket 也需要是volatile,因为它似乎是由openConnection() 的调用者创建的,但在while 循环中消耗。您可以考虑在 openConnection() 中将 active 设置为 true,并让 serverSocket 完全由接受线程管理。

  • clientSockets 看起来是一个集合。这将需要一个同步连接,因为它看起来被多个线程访问。同样,更好的模式是 closeAllClientConnections() 调用 just 设置一个布尔值,线程本身将执行关闭。这消除了使用集合等的任何竞争条件。

  • 看起来如果你是! terminating,你的接受线程将会旋转。至少你应该放一些Thread.sleep(100) 或其他东西来减慢它的速度。等待/通知会更好。

重要的是要意识到线程程序中“同时”发生的事情不仅仅是。它也与内存缓存有关。接受线程可能在一分钟前向clientSocketsArrayList1 添加了一些内容,如果列表未以某种方式同步,另一个线程可能看不到这些更改。更糟糕的是,ArrayList 的某些部分可能已在内存中更新,而其他部分可能会导致异常。要获得同步集合,您应该创建 ArrayList,例如:

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

听起来你应该阅读一些关于为什么需要同步的文档:

http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

【讨论】:

  • serverSocket 在那里创建,但它主要是重新实例化,因为它首先在 run() 方法中初始化(在第一次创建该共享时)。
  • 重新实例化仍然需要同步@Lovato。任何字段的任何更改如果被多个线程更改,都需要同步。此外,构造函数优化有很多复杂性,可能会导致套接字被构造但未完全初始化。如果可以的话,你应该避免在一个线程中构建它并在另一个线程中使用它。
  • clientSockets 是客户端套接字的 ArrayList。当我禁用共享时,我必须关闭连接到该共享的所有客户端,因此我将它们存储在一个集合中,调用 closeAllConnections() 并关闭每个客户端的所有连接,然后将它们从列表中删除。
  • 我理解这个@Lovato,但因为接受线程也添加到ArrayList,所以无论何时使用列表,都需要将它放在synchronized 块中。我在答案中添加了一些必读内容。
  • @Lovato 请至少花 30 分钟阅读我发布的链接。这很重要,否则你会编写错误的代码,这些代码有时可能有效,但有时会失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 2015-11-02
  • 1970-01-01
  • 2020-12-28
  • 2018-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多