Java: 两线程应用程序中UI线程无法恢复

4

我正在构建一个Java应用程序,使用两个线程:

第一个线程是关于应用程序的用户界面以及通过蓝牙线程接收命令的处理。

蓝牙线程是等待机器人连接并处理通信的蓝牙服务器。

目前为止,UI线程处于wait()状态,直到蓝牙线程收到新消息进行处理。

问题在于,我可以从蓝牙线程跟踪到notify/notifyAll的调用,但我的UI没有恢复其活动状态。

我现在确定自己对同步线程的正确管理方式有所误解,但我无法弄清楚软件中出了什么问题。

以下是UI代码:

package mapper;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;

public class MapperUI extends JFrame implements Runnable {

private ArrayList<String> messageArray;

public MapperUI(){
        super();        
        build();
        this.setVisible(true);
        new Thread(this).start();
}

private void build(){
    setTitle("SLAM Mapper");
    setSize(600,500);
    setLocationRelativeTo(null);
    setResizable(true);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setContentPane(buildContentPane());
}   
private JPanel buildContentPane(){
    JPanel main = new JPanel();
    main.setLayout(new BorderLayout());

    //TODO Implements auto-generated map after bluetooth communication
    MapPanel map = new MapPanel();
    main.add(map,BorderLayout.CENTER);

    //TODO This fields will be buildt with stored message
    JTable positions = new JTable(15,2);
    main.add(positions,BorderLayout.EAST);

    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new FlowLayout());

    JButton bouton = new JButton("Start");
    buttonPanel.add(bouton);

    JButton bouton2 = new JButton("Send");
    buttonPanel.add(bouton2);

    main.add(buttonPanel,BorderLayout.SOUTH);

    return main;
}

public synchronized void run(){
    MapperCom bt = new MapperCom();
    while(true){
        try {
            System.out.println("Mapper is Waiting......");
            wait();
            String message = bt.getMessage();
            this.messageArray.add(message);
            bt.setNextCommand(processMessage(message));
            notifyAll();
            System.out.println("Mapper Notify");
            build();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }
}

public String processMessage(String message){
    String command = "";
    //TODO Build a response
    command = "fffff\n";
    return command;
}
}

以下是蓝牙服务:

package mapper;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

public class MapperCom extends Thread {
public final UUID uuid = new UUID(                              
        "27012f0c68af4fbf8dbe6bbaf7aa432a", false);
public final String name = "Server";                       
public final String url  =  "btspp://localhost:" + uuid         
                            + ";name=" + name 
                            + ";authenticate=false;encrypt=false;";
private LocalDevice local ;
private StreamConnectionNotifier server ;
private StreamConnection conn ;
private DataInputStream din ;
private DataOutputStream dout ;

private String command;
private String message;

public MapperCom(){
     try {
        this.command = "";
        this.message = "";

        System.out.println("Setting device to be discoverable...");
        local = LocalDevice.getLocalDevice();
        local.setDiscoverable(DiscoveryAgent.GIAC);
        System.out.println("Start advertising service...");

        server = (StreamConnectionNotifier)Connector.open(url);
        System.out.println("Waiting for incoming connection...\n");

        conn = server.acceptAndOpen();
        System.out.println("Client Connected...");
        din = new DataInputStream(conn.openInputStream());
        dout = new DataOutputStream(conn.openOutputStream());

        new Thread(this).start();

        } catch (Exception  e) {
            System.out.println("Exception Occured: " + e.toString());
        }
}

@Override
public synchronized void run(){
    System.out.println("Bluetooth Thread Started");
    while(true){
        try {
            String cmd = "";
            char c;
            System.out.println("Waiting for message");
            while (((c = din.readChar()) > 0) && (c!='\n') ){
                System.out.println("Char received :"+c);
                cmd = cmd + c;
            }

            storeMessage(cmd);

            System.out.println("Bt Notify......");
            notifyAll();
            System.out.println("Bt is Waiting for a command from mapper......");
            wait();
            sendResponse();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public void sendResponse(){
    try {
        dout.writeChars(command);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public synchronized String getMessage(){
    return this.message;
}

public synchronized void storeMessage (String data){
    this.message = data;
    System.out.println("Received " + data);
}
public synchronized int setNextCommand (String data){
    int result = -1;
    //TODO Implement proper protocol
    this.command = data;
    System.out.println("Sending " + data);

    return result;
}
}

绝对不要将UI线程置于等待状态。这会冻结您的GUI界面。 - Marko Topolnik
3个回答

5

我认为当你调用notifyAll()时,它会调用this.notifyAll()。如果在同一个类中没有其他元素正在等待,那么你可以共享相同的对象在两个类之间,并调用obj.wait()obj.notifyAll()。这将起作用。


1
以下是一个示例程序。基本上,如果您想使用wait和notify进行通信,则需要在2个线程之间设置一些常见锁。
package ravi.tutorial.java.threads;

public class TestThreads {

    /**
     * @param args
     */
    public static void main(String[] args) {
        CommonLock commonLock = new CommonLock();
        Thread1 thread1 = new Thread1(commonLock);
        Thread2 thread2 = new Thread2(commonLock);
        thread1.start();
        thread2.start();
    }
}

/*
 * Common monitor lock between both threads, used for communication using wait
 * notify.
 */
class CommonLock {

}

// Extending Thread instead of Runnable as its just a test
class Thread1 extends Thread {

    private CommonLock commonLock;

    public Thread1(CommonLock commonLock) {
        this.commonLock = commonLock;
    }

    public void run() {
        System.out.println("Started thread 1");
        System.out.println("waiting thread 1");
        try {
            // TO wait on commonLock, first need to get lock on commonLock. SO
            // put synchronized block of commonLock.
            synchronized (commonLock) {
                commonLock.wait();
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("DONE waiting thread 1 as I got notification from THread 2");
    }

}

class Thread2 extends Thread {

    private CommonLock commonLock;

    public Thread2(CommonLock commonLock) {
        this.commonLock = commonLock;
    }

    public void run() {
        System.out.println("Running thread 2");

        try {
            System.out.println("Sleeping thread 2");
            // Just take gap of 2 sec before notifying.
            Thread.sleep(2000);

            // TO notify on commonLock, first need to get lock on commonLock. SO
            // put synchronized block of commonLock.
            synchronized (commonLock) {
                System.out.println("Notifying thread 2");
                commonLock.notifyAll();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

非常感谢您提供这个完整的实现。 - A. Chavy

0

看这个: 线程教程

  • wait( ) 告诉调用线程放弃监视器并进入休眠状态,直到其他线程进入相同的监视器并调用 notify( )。

  • notify( ) 唤醒在同一对象上调用 wait( ) 的第一个线程。

  • notifyAll( ) 唤醒在同一对象上调用 wait( ) 的所有线程。最高优先级的线程将首先运行。

这是 Java 线程概念的主要误解。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接