-1

在我的程序中,当用户输入一个数字时,程序会通过套接字将该数字发送到服务器,然后服务器将与该数字匹配的数据发回。数字代表服务水平。将 IncomingReader() 实例作为其可运行对象的线程然后读取从服务器发送的内容,将其存储为 arraylist(details)。然后我使用细节数组列表中的数据创建类 MyClients 的对象。我的问题是创建对象的循环在从服务器读取数据的线程运行之前运行。如何使从服务器读取的线程在创建对象的循环之前运行?代码如下:(为了简洁起见,我删除了 GUI 的代码)

public class SearchClients {

JFrame frame;
private JTextField textField;
private JTextField textField_1;
private JTextField textField_2;
private JTextField textField_3;


BufferedReader reader;
PrintWriter writer;
Socket sock;

 static ArrayList<String> details = new ArrayList<String>();

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                SearchClients window = new SearchClients();
                window.frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

public SearchClients() {
    initialize();
}

private void initialize() {

    setUpNetworking();
    Thread readerThread = new Thread(new IncomingReader());
    readerThread.start();


    JButton btnSearchByService = new JButton("Search By Service Level");

    btnSearchByService.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            searchByServiceLevel();
        }
    });

}

public void searchByServiceLevel() {

    try {
        writer.println("SEARCH BY SERVICE LEVEL");
        writer.println(textField_1.getText());
        writer.flush();


    }
    catch (Exception ex) {
        ex.printStackTrace();
    }



    JPanel nameSearchPane = new JPanel(new BorderLayout());

    frame.setExtendedState(Frame.MAXIMIZED_BOTH);
    frame.setContentPane(nameSearchPane);
    frame.invalidate();
    frame.validate();

    String[] columns = {"Name", "Phone Number", "Address", "District", "County", "Village", "Installation Date", "Energy Store", "Service Level", "Account Balance", "Months Owed", "Next Payment Date"};

    ArrayList<MyClients> clientDetails = new ArrayList<MyClients>();
    for(int y = 0; y < details.size(); y++) {
        MyClients client = new MyClients();
        client.name = details.get(y);
        client.phone_number = details.get(++y);
        client.address = details.get(++y);
        client.district = details.get(++y);
        client.county = details.get(++y);
        client.village = details.get(++y);
        client.installation_date = details.get(++y);
        client.energy_store = details.get(++y);
        client.service_level = details.get(++y);
        client.next_payment_date = details.get(++y);
        client.account_balance = details.get(++y);
        client.months_owed = details.get(++y);
        clientDetails.add(client);

    }

    details.clear();

//      Check if any data was returned from the database
    if(clientDetails.isEmpty()) {
        JOptionPane.showMessageDialog(frame, "A client with that service level was not found.\n Try service level: 1, 2, 3 or 4.");

        frame.setVisible(false);
        SearchClients search = new SearchClients();
        search.frame.setVisible(true);
    }

    String[][] clients = new String[100][100];

    for(int x = 0; x < clientDetails.size(); x++) {
        clients[x][0] = clientDetails.get(x).name;
        clients[x][1] = clientDetails.get(x).phone_number;
        clients[x][2] = clientDetails.get(x).address;
        clients[x][3] = clientDetails.get(x).district;
        clients[x][4] = clientDetails.get(x).county;
        clients[x][5] = clientDetails.get(x).village;
        clients[x][6] = clientDetails.get(x).installation_date.toString();
        clients[x][7] = clientDetails.get(x).energy_store;
        clients[x][8] = clientDetails.get(x).service_level;
        clients[x][9] = clientDetails.get(x).account_balance;
        clients[x][10] = clientDetails.get(x).months_owed;
        clients[x][11] = clientDetails.get(x).next_payment_date.toString(); 

    }

    JTable table = new JTable(clients, columns);
    JScrollPane tableContainer = new JScrollPane(table);
    nameSearchPane.add(tableContainer, BorderLayout.CENTER);

}

private void setUpNetworking() {
    try {
        sock = new Socket("127.0.0.1", 5000);
        InputStreamReader streamReader = new InputStreamReader(
                sock.getInputStream());
        reader = new BufferedReader(streamReader);
        writer = new PrintWriter(sock.getOutputStream());
        System.out.println("Networking established");
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}


class IncomingReader implements Runnable {
    public void run() {
        String message;
        try {
            while ((message = reader.readLine()) != null) {
                details.add(message);

            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

class MyClients {
    String name = "";
    String phone_number = "";
    String address = "";
    String district = "";
    String county = "";
    String village = "";
    String installation_date = "";
    String energy_store = "";
    String service_level = "";
    String next_payment_date = "";
    String account_balance = "";
    String months_owed = "";
    String clientID = "";
}

}
4

4 回答 4

7

为什么将它们放在不同的线程中?

通常,如果 B 的执行依赖于 A 的执行,那么将 A 和 B 在单独的线程中是您不需要的开销,它们实际上是顺序的。

现在已经不碍事了,看起来您正在尝试编写服务器。通常,您会将“服务器”作为其自己的应用程序运行。它会打开一个套接字,并等待输入。在输入时,它会启动一个工作线程(以便服务器可以继续接收套接字上的连接)。然后通过套接字将信息发送回调用程序。调用程序将信息发送到服务器,并“等待”直到信息返回。

所以你可能想做一些事情。

  • 您可能希望您的“线程”成为实际不同的应用程序
  • 您需要通过以下方式在发送线程中“等待”:

    • 某种形式的通信响应阻塞
    • 可以在响应时调用的回调对象
    • 在您的应用程序循环中查询响应的东西。

作为最后的评论,在任何时候你都需要在线程 B 之前运行线程 A,你的线程错误。需要停下来看看为什么:)

于 2013-08-19T01:05:47.247 回答
4

如何使从服务器读取的线程在创建对象的循环之前运行?

这与线程“运行顺序”无关,与对象的状态有关。实际上,我认为您对线程顺序的关注使您无法思考问题的真实性质。您需要使您的程序根据其状态对传入数据进行适当的行为,该状态根据接收到的内容和正在执行的操作而变化。现在谈谈你有什么具体的状态问题:

生产者-消费者问题:

您的问题是典型的生产者-消费者问题,因此您要做的不是使用详细信息的 ArrayList,而是使用队列。使用得当,当Details队列为空时,consumer、你的client及其对象生产循环会被阻塞,等待Details到达后再创建对象。

顺便说一句,无论您使用什么集合或技术,您的 Details 集合绝对不应该是 static。这将限制您以任何 OOP 方式使用集合和相关方法的能力。

于 2013-08-19T01:13:15.393 回答
2

您可以使用joinclass 的方法Thread。它将等到线程完成执行然后返回。之后,您可以启动第二个线程。

于 2013-08-19T00:57:14.487 回答
1

我不确定如何确保 A 始终在 B 之前运行,但有一种方法可以确保 B 始终在 A 之后运行。

使用CountDownLatch.

看下面这个修改的例子主线程会等待并阻塞,直到cacheService线程启动。

    public class CountDownLatchDemo {

    public static void main(String args[]) {
       final CountDownLatch latch = new CountDownLatch(1);
       Thread cacheService = new Thread(new Service("CacheService", 1000, latch));       
       cacheService.start(); //separate thread will initialize CacheService

       try{
            latch.await();  //main thread is waiting on CountDownLatch to finish
            System.out.println("All services are up, Application is starting now");
       }catch(InterruptedException ie){
           ie.printStackTrace();
       }

    }

}

/**
 * Service class which will be executed by Thread using CountDownLatch synchronizer.
 */
class Service implements Runnable{
    private final String name;
    private final int timeToStart;
    private final CountDownLatch latch;

    public Service(String name, int timeToStart, CountDownLatch latch){
        this.name = name;
        this.timeToStart = timeToStart;
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(timeToStart);
        } catch (InterruptedException ex) {
            Logger.getLogger(Service.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println( name + " is Up");
        latch.countDown(); //reduce count of CountDownLatch by 1
    }

}

Output:
CacheService is Up
All services are up, Application is starting now
于 2013-08-19T01:05:08.717 回答