【问题标题】:java.lang.IllegalThreadStateException: Thread already started error when using selectors with SocketChanneljava.lang.IllegalThreadStateException:将选择器与 SocketChannel 一起使用时,线程已启动错误
【发布时间】:2014-11-18 07:57:07
【问题描述】:

我正在开发一个 Android 应用程序,在该应用程序和我的服务器之间会有大量的网络通信。

为了实现这一点,我使用SocketChannelSelector 来执行非阻塞IO。

我选择的设计是,将有一个BlockingQueue 'NetworkIOManager' 线程将在其上等待。该应用程序的其他线程将向该BlockingQueue 发布消息,NetworkIOManager 将接收这些消息并将其发送到另一个线程AsyncRequestHandlerThread

因此NetworkIOManager 线程的主要职责是从BlockingQueue 中挑选消息并将它们委托给AsyncRequestHandlerThread 以发送请求并接收响应。

NetworkIOManager.java 的代码:

public class NetworkIOManager implements Runnable
{
    private AsyncRequestHandlerThread handlerThread = null; 

    /*
     * 
     * some code here
     * 
     */

    private void vSendRequestUsingSocketChannel(String pTargetURL, int pTargetPort, String pRequestXML, boolean pUseSameConn) {

         // if thread is not created, initialize the thread
         if(handlerThread == null) {
             handlerThread = new AsyncRequestHandlerThread();
         }

         // create a channel to send the request and register it with the selector
         AsyncRequestHandlerThread.createChannelWithSelector(pTargetURL, pTargetPort, pRequestXML);

         // if thread is not started, start it.
         if(!handlerThread.isAlive())
              handlerThread.start();
    }
}

AsyncRequestHandlerThread 基本上为要发送的每个请求创建一个SocketChannel,并使用Non-Blocking 配置并将其注册到与该线程关联的单个Selector

AsyncRequestHandlerThread.java 的代码:

public class AsyncRequestHandlerThread extends Thread {

private static Selector selector = null;

public AsyncRequestHandlerThread() {

    if(selector ==  null)
        vSetSelector();     
}

private static void vSetSelector()
{
    try {           
        selector = Selector.open();
    } 
    catch (IOException e) {
        e.printStackTrace();            
    }
}

public static Selector getSelector()
{
    return selector;
}

public static void createChannelWithSelector(String pTargetURL, int pTargetPort, String pRequestXML) {

    try {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress(pTargetURL, pTargetPort));
        socketChannel.register(selector, SelectionKey.OP_CONNECT, pRequestXML);
    } 
    catch (IOException e) {
        e.printStackTrace();
    }               
}

public void run() {

    try {

        //  Wait for events with TIMEOUT : 30 secs
        while (selector.select(30000) > 0) {

            try {

                // Get list of selection keys with pending events
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

                // Process each key at a time
                while (iterator.hasNext()) {

                    // Get the selection key
                    SelectionKey selKey = (SelectionKey)iterator.next();

                    // Remove it from the list to indicate that it is being processed
                    iterator.remove();

                    if (selKey.isValid() && selKey.isConnectable()) {

                        // Get channel with connection request
                        SocketChannel sChannel = (SocketChannel)selKey.channel();                    
                        boolean success = sChannel.finishConnect();

                        if (success) {
                            sChannel.register(selector, SelectionKey.OP_WRITE, selKey.attachment());
                        }

                        else {

                            // An error occurred; handle it 

                            // Unregister the channel with this selector
                            selKey.cancel();

                        }
                    }

                    else if(selKey.isValid() && selKey.isWritable()) {

                        SocketChannel sChannel = (SocketChannel)selKey.channel();

                         // See Writing to a SocketChannel
                        ByteBuffer requestBuffer = null;

                        requestBuffer = ByteBuffer.wrap(selKey.attachment().toString().getBytes(Charset.forName("UTF-8")));                     
                        sChannel.write(requestBuffer);  

                        sChannel.register(selector, SelectionKey.OP_READ);
                    }
                    else if (selKey.isValid() && selKey.isReadable()) {


                        // Get channel with bytes to read
                        SocketChannel sChannel = (SocketChannel)selKey.channel();

                        // See Reading from a SocketChannel
                        ByteBuffer responseBuffer = ByteBuffer.allocate(15);


                        while(sChannel.read(responseBuffer) > 0)    {

                            String responseString = new String(responseBuffer.array(), Charset.forName("UTF-8"));
                            Log.d("STATS", responseString);
                        }   

                        sChannel.close();
                    }                               
                }
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
            catch(CancelledKeyException e) {
                e.printStackTrace();
            }
        }           
    }
    catch(IOException ex) {
        ex.printStackTrace();
    }
}   
}

我面临的问题是,当我在设备上运行应用程序时,handlerThread.start();NetworkIOManager 的行 java.lang.IllegalThreadStateException 出现异常。当我调试它时,应用程序运行良好。

我无法理解问题出在哪里以及如何解决?

有什么建议吗?

【问题讨论】:

  • 异常信息不是一目了然吗?
  • 您没有正确处理finishConnect() 返回false。如果它返回false,您应该继续选择。如果它抛出异常它已经失败,你应该关闭通道,而不仅仅是取消密钥。
  • 是的,我明白了,但问题是为什么 if(!handlerThread.isAlive())true,即使线程已经在运行
  • 感谢@EJP 指出这一点,我会纠正的。但我不认为这会造成我面临的问题。
  • 我没说是。如果我这么认为,我会发布它作为答案。事实上,这是一条评论。

标签: java android multithreading socketchannel


【解决方案1】:

您正在尝试在线程退出后启动它。你需要重新思考你的逻辑。它目前假设只有一个这样的线程并且它永远不会退出,这是不正确的。

【讨论】:

  • 我无法理解为什么该线程正在退出?它应该接收注册到Selector 的所有通道的事件并继续处理它们。
  • @Mayank 你可以尝试用 getState() 重写对 isAlive() 的检查。 if (handlerThread.getState() == Thread.State.NEW) 然后您还可以打印线程状态以了解问题是在线程启动期间并发还是已终止,然后您尝试启动它。
  • @helleye 我已经检查过了,基本上线程在第二次遇到条件时被终止
  • @Mayank 那么你应该听从 EJP 的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-22
  • 1970-01-01
相关资源
最近更新 更多