从循环线程返回多个值而不在Java中杀死它

时间:2015-04-06 14:12:06

标签: java mysql multithreading

我正在尝试实现一个在线订购系统,它将通过Java中的MySQL工作。 使用MVC我正在尝试实现一个循环类,它将持续检查数据库表中的更改,这些更改将在到达时保存订单。现在,对于我得到的每个订单,我想将其显示为JFrame作为"框"。

为此,我使用一个Callable线程来循环名为OrderCheck.java的Class。然后从Controller调用此函数,如下所示。

public ArrayList<Order> StartOrderChecking() {
    OrderCheck OCObj = new OrderCheck();
    service = Executors.newFixedThreadPool(1); //We want one thread
    task = service.submit(OCObj);   //Submit the thread
    try {
            orderList = task.get();

        } catch (final InterruptedException ex) {
            ex.printStackTrace();
        } catch (final ExecutionException ex) {
            ex.printStackTrace();
        }
    return orderList;
}

线程函数看起来有点像......

public ArrayList<Order> call() {
    try {
        while (true) {
            System.err.println("Thread looping");
            ConnectToDB();
            query = "SELECT * FROM mb_orders";
            stmt = con.createStatement();
            rs = stmt.executeQuery(query);
            orderList = new ArrayList<Order>();
            while (rs.next()) {
                int ID = rs.getInt("ID");
                String email = rs.getString("email");
                String order = rs.getString("order");
                String orderinfo = rs.getString("orderinfo");
                int phone = rs.getInt("phone");

                newOrder = new Order(ID,email,order,orderinfo,phone); //We create and Order object
                orderList.add(newOrder); // This is my Arraylist that i want to return back to the view
                return orderList;
            }

            stmt.close();
            con.close();
            Thread.sleep(Time);
        }
    } catch (Exception e) {
    }
    return null;
}

代码只是循环直到程序终止,每隔3秒我检查数据库是否有新订单到达,如果存在则将它们存储在ArrayList中并返回。 现在我面临的问题是这个实现只会返回一次,然后线程就会死掉,我想以某种方式从DB继续将dataupdates传回Controller,然后将其发送回View,以便我可以将它显示在我的GUI。

感谢任何帮助!

我完全可以理解我的实现是否有问题并且会接受任何帮助来修复实现。我现在可以看到问题还在于如何在不停止整个程序的情况下将数据从Controller连续移动到视图。

此致

编辑:添加此内容是因为我觉得需要对此问题进行一些澄清。 我使用MVC模式,这意味着我有3个不同的抽象,M代表模型抽象,它包含与应用程序相关的所有单独的类,V代表View抽象,它只有用户的代码可以从用户交互中看到和调用。 C代表Controller抽象,它从View中的用户交互中获取调用,并进一步将它们分发到模型中的正确类。

现在在我的情况下,我有一个在视图抽象中的EDT中创建的GUI,上面的SQL-Conituous-loop-feth-connection-class-thing在模型(ofcourse)中。我希望这个线程以某种方式继续循环,同时不断获取包含SQL数据库中所有相关命令的ArrayList,然后将它们发送回Controller OR! (如果可能的话,这听起来更好),直接从循环线程刷新View Abstraction中的主GUI。

所以简单地说: - 我如何实现一个循环线程,可以更新View中存在的GUI,以便在主框架中显示相关的新订单。

下面是我创建GUI的方式和位置,以及我如何调用Controller来启动线程:

public class View extends JFrame {
. 
. 
.
public View(String UN) {
    setTitle("ExpressFood Admin Panel: " + UN);
    setSize(800, 800);
    setLocationRelativeTo(null); //Centered JFrame...
    setResizable(false);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Panel Setup
    JPanel mainPanel = new JPanel();
    add(mainPanel);
    mainPanel.setLayout(null);
    JMenuBar mainMenu = new JMenuBar();
    //Build test menu if needed...
    JMenu menu = new JMenu("A Menu");
    mainMenu.add(menu);
    setJMenuBar(mainMenu);
}
.
.
private void StartSim() throws SQLException, Exception { 
//Show the main frame to the user
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            JFrame mainFrame = new View(UN);
            mainFrame.setVisible(true);
        }
    });
orderList = controller.StartOrderChecking(); //Call to Controller for OrderCheck.java
}

以下代码是我如何从Controller调用OrderCheck.java类:

public ArrayList<Order> StartOrderChecking() {
    OrderCheck OCObj = new OrderCheck();
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    executor.scheduleWithFixedDelay(OCObj, 1, 3, TimeUnit.SECONDS);

    orderList = OCObj.sendToEDTForUpdate();
    return orderList;
}

4 个答案:

答案 0 :(得分:0)

不要退货。您已经知道如何创建线程池并在其中执行线程,因此每次数据库查询返回一个订单时,只需创建一个异步线程来执行该命令。并且只在程序关闭时退出无限循环。

答案 1 :(得分:0)

您不需要ExecutorService,需要SwingWorker

让我们检查SwingWorker的工作原理:

new SwingWorker<Void, Order>() {
    @Override
    protected Void doInBackground() throws Exception {
        //publish a chunk, this will get picked up for processing
        publish(newOrder);
        //we need to return null as the method is Void
        return null;
    }

    @Override
    protected void process(List<Order> chunks) {
        //process new chunks on the EDT
    }

    @Override
    protected void done() {
        //Thread is done, probably a problem
    }
};

因此,您必须了解有关Swing的一些事项。它不是线程安全的,因此您必须只访问EDT上的Swing组件。这非常重要。

那么,SwingWorker做的是doInBackground方法在某个后台线程上运行。此方法完成后,done方法在EDT上称为 。当您在publish方法中调用doInBackground时,已发布的对象将传递给process方法,该方法再次在EDT上调用

因此,除非您知道自己在做什么,否则应该使用已经存在的构造来实现工作线程与EDT之间的同步。

所以,在你的例子中:

new SwingWorker<Void, Order>() {
    @Override
    protected Void doInBackground() throws Exception {
        while (true) {
            System.err.println("Thread looping");
            ConnectToDB();
            query = "SELECT * FROM mb_orders";
            stmt = con.createStatement();
            rs = stmt.executeQuery(query);
            while (rs.next()) {
                int ID = rs.getInt("ID");
                String email = rs.getString("email");
                String order = rs.getString("order");
                String orderinfo = rs.getString("orderinfo");
                int phone = rs.getInt("phone");

                final Order newOrder = new Order(ID, email, order, orderinfo, phone);
                publish(newOrder);
            }

            stmt.close();
            con.close();
            Thread.sleep(Time);
        }
        return null;
    }

    @Override
    protected void process(List<Order> chunks) {
        for (Order chunk : chunks) {
            //display chunk
        }
    }

    @Override
    protected void done() {
        //Thread is done, probably a problem
        try {
            get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
};

现在,您需要确保永远不会拥有空的catch块。 必须以某种方式处理异常 - 记录或显示它。

事实上,空挡块是许多软件公司发出官方口头警告的原因。

答案 2 :(得分:0)

以下是我建议的方式

  1. 创建一个Runnable,在那里执行db查询代码,并在获得Order后将其发送到gui上显示,显示代码应该在EDT中。

    类MyRunnable实现了Runnable {

    @Override
    public void run() {
        List<Order> orders = connectToDBAndGetOrders();
        sendToEDTForUpdate(orders);
        return ;
    }
    
    private void sendToEDTForUpdate(final List<Order> orders) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                for(Order order : orders) {
                    //display on gui
                }
            }
        });
    }
    
    private List<Order> connectToDBAndGetOrders() {
        String query;
        Statement stmt;
        ResultSet rs;
        List<Order> orderList;
        Connection con;
        try {
            con = ConnectToDB();
            query = "SELECT * FROM mb_orders";
            stmt = con.createStatement();
            rs = stmt.executeQuery(query);
            orderList = new ArrayList<Order>();
            while (rs.next()) {
                int ID = rs.getInt("ID");
                String email = rs.getString("email");
                String order = rs.getString("order");
                String orderinfo = rs.getString("orderinfo");
                int phone = rs.getInt("phone");
    
                Order newOrder = new Order(ID,email,order,orderinfo,phone); //We create and Order object
                orderList.add(newOrder); // This is my Arraylist that i want to return back to the view
                return orderList;
            }
    
            stmt.close();
            con.close();
        } catch (Exception e) {
            //exception handling
        }
        return null;
    }
    

    }

  2. 安排此Runnable每3秒或根据需要执行一次。请节点有两种方法scheduleAtFixedRate和scheduleWithFixedDelay,我建议使用scheduleWithFixedDelay。

    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    MyRunnable c = new MyRunnable();
    
    executor.scheduleWithFixedDelay(c, 1, 3, TimeUnit.SECONDS);
    
  3. 编辑示例完整代码

    没有数据库连接。当然,这不是生产就绪代码,只是为了显示我的思考过程,

    package thread.executor;
    
    import java.awt.FlowLayout;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    
    public class MyFrame extends JFrame {
    
        public static void main(String[] args) {
    
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    MyFrame frame = new MyFrame("Background Fetching");
    
                    Controller controller = new Controller(frame);
                    controller.startFetchingOrder();
                    frame.setSize(600, 600);
    
                    frame.setVisible(true);
                }
            } );
    
        }
    
        public MyFrame(String title) {
            super(title);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            FlowLayout layout = new FlowLayout();
            getContentPane().setLayout(layout);
            getContentPane().add(new JButton("Fixed"));
        }
    
        public void addNewButton(String text) {
            getContentPane().add(new JButton(text));
            validate();
        }
    
    
        static class Controller {
    
            ScheduledExecutorService executor;
            MyFrame view;
    
            public Controller(MyFrame view) {
                this.view = view; 
            }
    
            public void startFetchingOrder() {
                executor = Executors.newScheduledThreadPool(1);
                MyRunnable c = new MyRunnable(this);
    
                executor.scheduleWithFixedDelay(c, 1, 3, TimeUnit.SECONDS);
            }
    
            public void stopFetchingOrder() {
                if(executor != null)
                    executor.shutdown();
            }
    
            public void sendToEDTForUpdate(final List<Order> orders) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        for(Order order : orders) {
                            System.out.println(Thread.currentThread().getName()+"  adding new order "+order.getId());
                            view.addNewButton(""+order.getId());
                        }
                    }
                });
            }
        }
    
        static class MyRunnable implements Runnable {
    
            Controller controller;
    
            public MyRunnable(Controller controller) {
                this.controller = controller;
            }
            @Override
            public void run() {
                List<Order> orders = connectToDBAndGetOrders();
                controller.sendToEDTForUpdate(orders);
                return ;
            }
    
            static AtomicInteger counter = new AtomicInteger(0);
    
            private List<Order> connectToDBAndGetOrders() {
                List<Order> list = new ArrayList<Order>();
    
                int count = (int)(5 * Math.random());
    
                for (int i = 0; i < count; i++) {
                    Order order = new Order((counter.incrementAndGet()*1000)+i, null, null, null, 0);
                    System.out.println(Thread.currentThread().getName()+"  got new order "+order.getId());
                    list.add(order);
                }
    
                System.out.println(Thread.currentThread().getName()+"  returningOrders "+list.size());
                return list;
            }
        }
    }
    
    class Order {
        int id;
    
        public Order() { 
            id = 0;
        }
        public Order(int Id, String email, String order, String orderInfo, int phone) {
            this.id = Id;
        }
    
        public int getId() {
            return id;
        }
    }
    

答案 3 :(得分:0)

让我清楚地了解你想要做什么。 创建一个线程来连续查询数据库以更新呈现给某个视图的列表。

我注意到您正在return内拨打while-loop,这正在退出该方法。

我建议您使用newSingleThreadExecutor代替newFixedThreadPool,而使用CopyOnWriteArrayList

使用get方法检索更新orderList。

private  CopyOnWriteArrayList<Order> orderList = new CopyOnWriteArrayList<>();
ExecutorService service = null;

...

public void StartOrderChecking() {
    OrderCheck OCObj = new OrderCheck();
    if(service.isShutdown()){
        service = Executors.newSingleThreadExecutor();
    }else{
        service.shutdownNow();
        service = Executors.newSingleThreadExecutor();
    }

    service.execute(OCObj);
}

...

public ArrayList<Order> getOrderList() {
    ArrayList rtnList = new ArrayList();
    for(Order order : orderList){
        rtnList.add(order);
    }
    return rtnList;
}

...

private class OrderCheck implements Runnable {

    @Override
    public void run() {
        try {
            while (true) {
                System.err.println("Thread looping");
                ConnectToDB();
                query = "SELECT * FROM mb_orders";
                stmt = con.createStatement();
                rs = stmt.executeQuery(query);
                orderList.clear();
                while (rs.next()) {
                    int ID = rs.getInt("ID");
                    String email = rs.getString("email");
                    String order = rs.getString("order");
                    String orderinfo = rs.getString("orderinfo");
                    int phone = rs.getInt("phone");

                    newOrder = new Order(ID, email, order, orderinfo, phone); //We create and Order object
                    orderList.add(newOrder); // This is my Arraylist that i want to return back to the view
                }

                stmt.close();
                con.close();
                Thread.sleep(Time);
            }
        } catch (Exception e) {
            //Log something
        }
    }

}

只需将OCObj.sendToEDTForUpdate()替换为getOrderList

即可
public ArrayList<Order> StartOrderChecking() {
    OrderCheck OCObj = new OrderCheck();
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    executor.scheduleWithFixedDelay(OCObj, 1, 3, TimeUnit.SECONDS);

    orderList = controller.getOrderlist();
    return orderList;
}

希望这个帮助和好运编程