如何知道Thread是否已在SWT环境中完成其任务?

时间:2017-07-20 19:17:21

标签: java multithreading swt

我从一些线程中获取结果时遇到了麻烦。 我解释了环境,我有一个带按钮的SWT shell。此按钮的侦听器调用Runnable,Runnable在其run()内部调用实例化N个线程以执行某些操作的方法。问题是:当所有计算终止时,如何在屏幕上显示消息对话框?我的代码是类似的

public void widgetSelected(SelectionEvent event) {
    Runnable t = new MyThread(params);
    executor.execute(t);
  }

在MyThread课程中我有

public void run(){
   myMethod();
}

public void myMethod(){
   for(int i =0; i<queue.length; i++){
      Runnable thread = new AnotherThread();
      executor.execute(thread);
   }
   executor.shutdown();
   if(executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS){
     //here I know all thread of kind AnotherThread have finished
   }
}

因此,在按钮侦听器的widgetSelected方法中,我想放置一些警告用户的内容,即侦听器调用的所有线程都已成功终止。 有没有办法知道这个?如果我将awaitTermination语句放在Listener中,shell就会响应并在屏幕上冻结。 希望有人可以帮助我。如果问题不明确,请告诉我。 谢谢大家。

2 个答案:

答案 0 :(得分:1)

这是“直截了当” - 但有点工作:你“只是”必须增强你的Runnables以某种方式表明他们的进展。

换句话说:ExecutorService接口不提供任何方法来计算完成了多少计划的“任务”。所以 - 如果您需要这些信息,您必须将其“烘焙”到Runnable中。

一个想法:提前创建Map<Runnable, Integer>。键是您的Runnable对象,值可以表示一些进度信息。首先将所有值都设置为0.然后将该映射传递给每个Runnable - Runnable只是在某些定义的时间点更新值。也许只有十步,或者只有四步。只要所有地图值都是100,你知道你已经完成了!这意味着你的主线程只是循环并每隔一秒/每分钟检查地图内容/ ...当然:这个额外的线程应该是事件调度程序线程。在执行此操作时,您不希望停止UI。

(当然:你应该使用ConcurrentHashMap来实现它)。

长话短说:这些信息已经可用 - 您的Runnable中的代码知道它在做什么,对吧?!因此,您“只”必须以某种方式使外部世界可以访问此信息。有很多选择可以做到这一点;以上只是达到目的的一种方式。

答案 1 :(得分:1)

我建议您查看使用属于Guava库的FutureCallbacks

这将允许您为您发起的每项任务创建ListenableFuture。在您的情况下,听起来这可能由Runnable表示,但您可以轻松使用Callable。当这些任务全部被触发时,您将最终获得这些ListenableFuture个对象的列表,这些对象可以“展平”为单个ListenableFuture,表示所有任务的完成。这是通过方法Futures.allAsList(...)完成的:

final List<ListenableFuture<T>> futures = ...
final ListenableFuture<List<T>> combinedFuture = Future.allAsList(futures);

既然您有一个ListenableFuture代表所有任务的完成,您可以通过添加FutureCallback来完成后轻松监听其完成情况:

Futures.addCallback(future, new FutureCallback<List<String>>() {
    @Override
    public void onFailure(final Throwable arg0) {
        // ...
    }

    @Override
    public void onSuccess(final List<String> arg0) {
        // ...
    }
}

现在,完成这些任务后,我们需要更新UI以通知用户。为此,我们必须确保UI更新发生在SWT UI线程上:

Display.getCurrent().asyncExec(new Runnable() {
    @Override
    public void run() {
        // Update the UI
    }
});

请注意,这可以在上面的onSuccess()方法中轻松完成,以便可以使用任务的结果。

总而言之,我们可以轻松地遍历少数ListeningExecutorService。submit(...)调用后台执行(以便不阻止UI线程 - 在下面的示例中,您可以自由地在任务在后台运行时在文本框中输入),获取所有ListenableFutures,并添加一个完成时调用的回调,这将回跳到UI线程以进行UI更新。

完整示例:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

public class CallbackExample {

    private final Display display;
    private final Shell shell;
    private final Text output;
    private final ListeningExecutorService executor;

    public CallbackExample() {
        display = new Display();
        shell = new Shell(display);
        shell.setLayout(new FillLayout());

        executor = MoreExecutors.listeningDecorator(Executors
                .newFixedThreadPool(20));

        final Composite baseComposite = new Composite(shell, SWT.NONE);
        baseComposite
                .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        baseComposite.setLayout(new GridLayout());

        output = new Text(baseComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
        output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        final Button button = new Button(baseComposite, SWT.PUSH);
        button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        button.setText("Start tasks");
        button.addSelectionListener(new SelectionAdapter() {

            @SuppressWarnings("synthetic-access")
            @Override
            public void widgetSelected(final SelectionEvent e) {
                // Start tasks when the button is clicked
                startTasks();
            }

        });

    }

    private void startTasks() {
        // Create a List to hold the ListenableFutures for the tasks
        final List<ListenableFuture<String>> futures = new ArrayList<ListenableFuture<String>>();
        // Submit all the tasks for execution (in this case 100)
        for (int i = 0; i < 100; i++) {
            final ListenableFuture<String> future = executor
                    .submit(new Callable<String>() {
                        @Override
                        public String call() throws Exception {
                            // Do the work! Here we sleep to simulate a long task
                            Thread.sleep(2000);
                            final long currentMillis = System
                                    .currentTimeMillis();
                            System.out.println("Task complete at "
                                    + currentMillis);
                            return "Task complete at " + currentMillis;
                        }
                    });
            // Add the future for this task to the list
            futures.add(future);
        }
        // Combine all of the futures into a single one that we can wait on
        final ListenableFuture<List<String>> future = Futures
                .allAsList(futures);
        // Add the callback for execution upon completion of ALL tasks
        Futures.addCallback(future, new FutureCallback<List<String>>() {

            @Override
            public void onFailure(final Throwable arg0) {
                System.out.println("> FAILURE");
            }

            @SuppressWarnings("synthetic-access")
            @Override
            public void onSuccess(final List<String> arg0) {
                System.out.println("> SUCCESS");
                // Update the UI on the SWT UI thread
                display.asyncExec(new Runnable() {

                    @Override
                    public void run() {
                        final StringBuilder sb = new StringBuilder();
                        for (final String s : arg0) {
                            sb.append(s + "\n");
                        }
                        final String resultString = sb.toString();
                        output.setText(resultString);
                    }

                });
            }

        });
    }

    public void run() {
        shell.setSize(200, 200);
        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        executor.shutdownNow();
        display.dispose();
    }

    public static void main(final String... args) {
        new CallbackExample().run();
    }

}