如何让Java线程始终在另一个线程之前运行?

时间:2013-08-19 00:53:19

标签: java multithreading static thread-safety static-members

在我的程序中,当用户输入一个号码时,程序通过套接字将该号码发送到服务器,服务器发回与该号码匹配的数据。该数字代表服务级别。具有IncomingReader()实例作为其runnable的线程然后读取从服务器发送的内容,并将其存储为arraylist(详细信息)。然后,我使用详细信息arraylist中的数据创建类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 个答案:

答案 0 :(得分:7)

为什么你在单独的线程中拥有它们?

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

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

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

  • 您可能希望将“线程”视为实际的不同应用程序
  • 您需要在发送线程中“等待”:

    • 通信响应中的某种形式的阻止
    • 一个可以在响应时调用的回调对象
    • 在您的应用程序循环中查询响应的内容。

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

答案 1 :(得分:4)

  

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

这与线程“运行顺序”无关,而与对象状态有关。事实上,我认为你专注于线程顺序会阻止你思考问题的本质。您需要使程序根据其状态进行适当的处​​理,具体取决于接收的内容和正在执行的操作。现在问你有什么特定的状态问题:

生产者 - 消费者问题:

你的是一个典型的生产者 - 消费者问题,所以你想要做的不是使用详细信息的ArrayList而是使用Queue。正确使用时,当Details的队列为空时,将阻止使用者,客户端及其对象生成循环,并在创建对象之前等待Details到达。

顺便说一句,无论您使用何种集合或技术,您的详细信息集都应绝对不是静态的。这将限制您以任何OOP方式使用集合和相关方法的能力。

答案 2 :(得分:2)

您可以使用课程join的{​​{1}}方法。它将等待线程完成执行然后返回。之后,您可以启动第二个帖子。

答案 3 :(得分:1)

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

使用CountDownLatch

请参阅以下this上修改的示例,主线程将等待并阻塞,直到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