使用Java套接字,锁和线程进行死锁

时间:2018-06-06 10:36:29

标签: java android multithreading sockets

我有两个Android设备,我使用操作系统的P2P管理器通过WiFi Direct连接。当设备连接时,我启动AsyncTask以在两个设备之间建立套接字连接。一旦套接字启动并运行(组所有者具有服务器套接字且非组所有者具有客户端套接字),我希望尽可能快地在两个设备之间交换浮动数组,并测量获得带宽估计所需的时间。 / p>

我这样做是让每个设备在建立套接字后立即创建SendThreadRecvThread,这些线程分别负责OutputStreamInputStream那个设备。他们立即等待锁定,我有一个TestThread,它按照正确的顺序唤醒它们,以完成他们的沟通。即TestThread唤醒SendThread唤醒RecvThread然后再唤醒TestThread,然后强制执行命令。

虽然这种方法似乎可以工作几秒钟(我有一个显示数据流的UI),但是这个过程最终会因为两个设备阻塞其recv线程等待输入流上的数据而停顿不前。

我尝试过的事情:
1.使用ObjectStream发送一个浮动容器以及DataStream来单独发送浮动 2.在线程池执行程序上使用AsyncTask来执行交换而不是线程。

上述两者在经过几秒的顺利交换后产生了相同的死锁结果。

我的问题是在这种情况下如何发生僵局?我使用缓冲流并在flush()进入睡眠状态之前调用SendThread。睡眠发送线程是否会以某种方式阻止缓冲数据的发送?似乎两个设备都已发送,但都没有收到其他数据。如果我离开设备大约5分钟左右,readFloat()的冻结呼叫最终完成,但随后又在下一次交换时挂起。

对于需要双向连续通信的P2P多人游戏来说,这必须是常见的操作。

代码如下所示,请注意socketSet只是封装了套接字(服务器或客户端,具体取决于运行代码的设备)及其流。

测试线程:

public void run()
{
    while (runFlag)
    {
        // Start timer
        long timeStart = System.nanoTime();

        // Create containers
        DataContainer containerArraySend = new DataContainer(10);
        DataContainer containerArrayRecv = new DataContainer(10);

        // Send and receive with other device //

        // Create a new latch
        workCounter = new CountDownLatch(containerArraySend.size());

        // Set the containers for the respective manager threads
        socketSet.sendThread.setDataContainer(containerArraySend);
        socketSet.recvThread.setDataContainer(containerArrayRecv);

        // Set the new latch
        socketSet.recvThread.setLatch(workCounter);

        synchronized (socketSets.lockB)
        {
            // Wake the send thread to start the exchange
            socketSets.lockB.notifyAll();
        }   

        // Wait until all the exchanges are complete and woken by recv thread
        workCounter.await();

        // Log time taken
        timeTaken = (double)(System.nanoTime() - timeStart) / 1000000.0;

    }
}

发送帖子:

// Send thread
public void run()
{
    // Call parent method to set the run flag
    super.run();

    // Immediately wait on thread creation until test thread releases it
    synchronized (socketSet.lockB)
    {
        socketSet.lockB.wait();
    }

    // Once released starts executing here
    while (runFlag)
    {
        // Writes to stream and immediate returns (non-blocking)
        for (int i = 0; i < dataContainer.size; i++)
        {
            socketSet.sendStream.writeFloat(dataContainer.dataBuffer[i]);
        }
        socketSet.sendStream.flush();

        synchronized (socketSet.lockC)
        {
            // Wake recv thread
            socketSet.lockC.notifyAll();
        }

        synchronized (socketSet.lockB)
        {
            // Sleep send thread until woken by test thread
            socketSet.lockB.wait();
        }
    }
}

Recv Thread:

// Recv thread
public void run()
{
    // Immediately lock on thread creation until send thread releases it
    synchronized (socketSet.lockC)
    {
        socketSet.lockC.wait();
    }

    // Once released starts executing here
    while (runFlag)
    {
        // Blocks until there is data to receive on the stream
        for (int i = 0; i < dataContainer.size; i++)
        {
            dataContainer.dataBuffer[i] = socketSet.recvStream.readFloat();
        }

        // Count down latch once recv complete (will wake test thread)
        latch.countDown();

        synchronized (socketSet.lockC)
        {
            // Recv thread sleeping until woken by send thread
            socketSet.lockC.wait();
        }
    }
}

0 个答案:

没有答案