java SwingWorker从doInBackground()启动runnables以及如何通知事件派发线程

时间:2021-04-03 17:28:33

just learnd the SwingWorker and have a question
( i have search for a answer to this but non specifically address this setup)

只是学习SwingWorker并有一个问题(我已经搜索了这个答案,但没有具体解决这个问题)

Im creating a small server that will have like max simultaneous 2-3 connections only.
Im using a Jframe that have the inner class SwingWorker

我创建一个小型服务器,只有最大同时2-3个连接。我使用的是具有内部类SwingWorker的Jframe

In the SwingWorker doInBackground() i have the:

在SwingWorker doInBackground()中我有:

while(true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  Future<Connection> future = es.submit(con , con ); 
  tasks.add(future);
 }

The Connection is a runnable and declared as a subclass in SwingWorker.

Connection是一个可运行的,并在SwingWorker中声明为子类。

Before the runnable has completed it write an entry in the SQL.
How can then this runnable before it dies send a heads-up to Jframe event dispatch thread.
and the Jframewill check the SQL for the new entry and display it to user.

在runnable完成之前,它在SQL中写入一个条目。那么在它死之前这个可运行的怎么能发送一个单挑到Jframe事件调度线程。并且Jframe将检查SQL以获取新条目并将其显示给用户。

what is best to do:

什么是最好的做法:

1 - create an interface witch all runnable can send messages to Jframe event dispatch thread.

1 - 创建一个界面,所有runnable都可以向Jframe事件派发线程发送消息。

2 - use SwingWorker insted of runnables for all new connections and have Done() call a method in server SwingWorker that call method in Jframe using EventQueue.invokeLater..

2 - 使用SwingWorker为所有新连接提供runnables,并让Done()在服务器SwingWorker中调用一个方法,该方法使用EventQueue.invokeLater在Jframe中调用方法。

3 - or use a PropertyChangeListener (somehow no sure)

3 - 或使用PropertyChangeListener(不知何故不确定)

4 - Let each runnables have s ref to Jframe and do EventQueue.invokeLater..

4 - 让每个runnables都参考Jframe并执行EventQueue.invokeLater。

3 个解决方案

#1


1  

The SwingWorker docs are pretty clear. You should subclass SwingWorker and perform the long task in the doInBackground() method. You should update the UI in the done() method.

SwingWorker文档很清楚。您应该将SwingWorker子类化并在doInBackground()方法中执行长任务。您应该在done()方法中更新UI。

It really is as easy as that.

它真的很简单。

Edit:
To make it clearer. Assuming your Connection class extends SwingWorker it does not need to implement Runnable and you do not need to explicitly provide a thread pool to run the workers in. Just put the contents of the run() method in doInBackground().

编辑:使其更清晰。假设您的Connection类扩展了SwingWorker,它不需要实现Runnable,也不需要显式提供一个线程池来运行worker。只需将run()方法的内容放在doInBackground()中即可。

Now your main loop looks something like this;

现在你的主循环看起来像这样;

while (true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  con.execute();
}

You seem to be submitting to an ExecutorService in your main loop. Is there a specific reason for this (note that a SwingWorker manages its own internal ThreadPoolExecutor for worker threads). Is it to limit the number of concurrent clients? If so there are other ways to accomplish this.

您似乎在主循环中提交到ExecutorService。是否有特定原因(请注意,SwingWorker为工作线程管理自己的内部ThreadPoolExecutor)。它是否限制并发客户端的数量?如果是这样,还有其他方法可以做到这一点。

#2


1  

I would go for the following: have a thread safe blocking queue or list in the parent thread that will be passed to worker threads. Once a task is done, a worker thread will post a message that contains the result entry ID into this blocking queue. The parent thread will block on the queue waiting for results from child threads. Whenever there is an element in the queue, the parent thread will take it and go get that data from DB and display it to user.

我会选择以下内容:在父线程中有一个线程安全阻塞队列或列表,它将传递给工作线程。任务完成后,工作线程会将包含结果条目ID的消息发布到此阻塞队列中。父线程将阻塞队列,等待子线程的结果。只要队列中有一个元素,父线程就会接受它并从DB获取该数据并将其显示给用户。

#3


1  

I tried to create an example of a SwingWorker that generates background worker threads, the results of which get published via a fixed pool ExecutorService and a CompletionService. I still have some questions regarding the thread safety of creating the worker threads inside of one thread, and then calling get on their futures inside of another thread (the SwingWorker background thread).

我尝试创建一个生成后台工作线程的SwingWorker示例,其结果通过固定池ExecutorService和CompletionService发布。我仍然有一些关于在一个线程内创建工作线程的线程安全性的问题,然后在另一个线程(SwingWorker后台线程)内部调用get的未来。

for example:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.*;

@SuppressWarnings("serial")
public class TestSwingWorker extends JPanel {
   public static final int POOL_SIZE = 4;
   private JTextArea tArea = new JTextArea(10, 30);
   private JButton doItBtn;

   public TestSwingWorker() {
      doItBtn = new JButton(new AbstractAction("Do It!") {
         public void actionPerformed(ActionEvent ae) {
            swingWorkerRunning(true);
            MySwingWorker mySW = new MySwingWorker();
            mySW.execute();
            tArea.append("SwingWorker started\n");
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(doItBtn);
      tArea.setEditable(false);
      tArea.setFocusable(false);

      setLayout(new BorderLayout());
      add(new JScrollPane(tArea), BorderLayout.CENTER);
      add(btnPanel, BorderLayout.SOUTH);
   }

   private class MySwingWorker extends SwingWorker<String, String> {
      @Override
      protected String doInBackground() throws Exception {
         ExecutorService execService = Executors.newFixedThreadPool(POOL_SIZE);
         final CompletionService<String> completionService = new ExecutorCompletionService<String>(
               execService);
         new Thread(new Runnable() {

            @Override
            public void run() {
               for (int i = 0; i < POOL_SIZE; i++) {
                  final int index = i;
                  completionService.submit(new Callable<String>() {
                     public String call() throws Exception {
                        Thread.sleep(2000 * index + 500);
                        return "Callable " + index + " complete";
                     }
                  });
                  try {
                     Thread.sleep(1000);
                  } catch (InterruptedException e) {
                  }
               }
            }
         }).start();

         for (int i = 0; i < POOL_SIZE; i++) {
            Future<String> f = completionService.take();
            publish(f.get());
         }

         return "Do in background done";
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            tArea.append(chunk + "\n");
         }
      }

      @Override
      protected void done() {
         try {
            tArea.append(get() + "\n");
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         } finally {
            swingWorkerRunning(false);
         }
      }
   }

   public void swingWorkerRunning(boolean running) {
      doItBtn.setEnabled(!running);
   }

   private static void createAndShowGui() {
      TestSwingWorker mainPanel = new TestSwingWorker();

      JFrame frame = new JFrame("TestSwingWorker");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Corrections most welcome!

更正最受欢迎!

#1


1  

The SwingWorker docs are pretty clear. You should subclass SwingWorker and perform the long task in the doInBackground() method. You should update the UI in the done() method.

SwingWorker文档很清楚。您应该将SwingWorker子类化并在doInBackground()方法中执行长任务。您应该在done()方法中更新UI。

It really is as easy as that.

它真的很简单。

Edit:
To make it clearer. Assuming your Connection class extends SwingWorker it does not need to implement Runnable and you do not need to explicitly provide a thread pool to run the workers in. Just put the contents of the run() method in doInBackground().

编辑:使其更清晰。假设您的Connection类扩展了SwingWorker,它不需要实现Runnable,也不需要显式提供一个线程池来运行worker。只需将run()方法的内容放在doInBackground()中即可。

Now your main loop looks something like this;

现在你的主循环看起来像这样;

while (true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  con.execute();
}

You seem to be submitting to an ExecutorService in your main loop. Is there a specific reason for this (note that a SwingWorker manages its own internal ThreadPoolExecutor for worker threads). Is it to limit the number of concurrent clients? If so there are other ways to accomplish this.

您似乎在主循环中提交到ExecutorService。是否有特定原因(请注意,SwingWorker为工作线程管理自己的内部ThreadPoolExecutor)。它是否限制并发客户端的数量?如果是这样,还有其他方法可以做到这一点。

#2


1  

I would go for the following: have a thread safe blocking queue or list in the parent thread that will be passed to worker threads. Once a task is done, a worker thread will post a message that contains the result entry ID into this blocking queue. The parent thread will block on the queue waiting for results from child threads. Whenever there is an element in the queue, the parent thread will take it and go get that data from DB and display it to user.

我会选择以下内容:在父线程中有一个线程安全阻塞队列或列表,它将传递给工作线程。任务完成后,工作线程会将包含结果条目ID的消息发布到此阻塞队列中。父线程将阻塞队列,等待子线程的结果。只要队列中有一个元素,父线程就会接受它并从DB获取该数据并将其显示给用户。

#3


1  

I tried to create an example of a SwingWorker that generates background worker threads, the results of which get published via a fixed pool ExecutorService and a CompletionService. I still have some questions regarding the thread safety of creating the worker threads inside of one thread, and then calling get on their futures inside of another thread (the SwingWorker background thread).

我尝试创建一个生成后台工作线程的SwingWorker示例,其结果通过固定池ExecutorService和CompletionService发布。我仍然有一些关于在一个线程内创建工作线程的线程安全性的问题,然后在另一个线程(SwingWorker后台线程)内部调用get的未来。

for example:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.*;

@SuppressWarnings("serial")
public class TestSwingWorker extends JPanel {
   public static final int POOL_SIZE = 4;
   private JTextArea tArea = new JTextArea(10, 30);
   private JButton doItBtn;

   public TestSwingWorker() {
      doItBtn = new JButton(new AbstractAction("Do It!") {
         public void actionPerformed(ActionEvent ae) {
            swingWorkerRunning(true);
            MySwingWorker mySW = new MySwingWorker();
            mySW.execute();
            tArea.append("SwingWorker started\n");
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(doItBtn);
      tArea.setEditable(false);
      tArea.setFocusable(false);

      setLayout(new BorderLayout());
      add(new JScrollPane(tArea), BorderLayout.CENTER);
      add(btnPanel, BorderLayout.SOUTH);
   }

   private class MySwingWorker extends SwingWorker<String, String> {
      @Override
      protected String doInBackground() throws Exception {
         ExecutorService execService = Executors.newFixedThreadPool(POOL_SIZE);
         final CompletionService<String> completionService = new ExecutorCompletionService<String>(
               execService);
         new Thread(new Runnable() {

            @Override
            public void run() {
               for (int i = 0; i < POOL_SIZE; i++) {
                  final int index = i;
                  completionService.submit(new Callable<String>() {
                     public String call() throws Exception {
                        Thread.sleep(2000 * index + 500);
                        return "Callable " + index + " complete";
                     }
                  });
                  try {
                     Thread.sleep(1000);
                  } catch (InterruptedException e) {
                  }
               }
            }
         }).start();

         for (int i = 0; i < POOL_SIZE; i++) {
            Future<String> f = completionService.take();
            publish(f.get());
         }

         return "Do in background done";
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            tArea.append(chunk + "\n");
         }
      }

      @Override
      protected void done() {
         try {
            tArea.append(get() + "\n");
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         } finally {
            swingWorkerRunning(false);
         }
      }
   }

   public void swingWorkerRunning(boolean running) {
      doItBtn.setEnabled(!running);
   }

   private static void createAndShowGui() {
      TestSwingWorker mainPanel = new TestSwingWorker();

      JFrame frame = new JFrame("TestSwingWorker");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Corrections most welcome!

更正最受欢迎!