如何在线程冻结时从外部停止Java中的线程?

时间:2014-03-02 11:17:46

标签: java multithreading swingworker

我阅读了很多关于Java Threads的内容,但我不确定最佳解决方案。

我创建了一个工作线程来访问一个php-Script(而php-Script访问一个mysql数据库)。如果以某种方式使用php-Script或mysql-database的服务器忙,则线程会在读取或发送操作中进行释放。因此,设置中断并让线程停止的概念本身不起作用。

现在我用ProgressMonitor创建了第二个工作线程。当用户单击ProgressMonitor的取消按钮时,冻结的第一个线程将被取消。 如果第一个Thread工作正常,它会取消第二个Thread。因此,两个线程可以相互抵消。

但这是最佳解决方案吗?是否有更好,更安全的方法来做到这一点?

    class ArbeiterErstelleTabellenmodell extends SwingWorker<TabellenmodellMitarbeiter, Object>
{
    ProgressMonitor anzeige;
    ErstelleTabellenmodellMitAnzeige fadenAnzeige;

    ArbeiterErstelleTabellenmodell(ProgressMonitor anzeige, ErstelleTabellenmodellMitAnzeige fadenAnzeige)
    {
        this.anzeige = anzeige;
        this.fadenAnzeige = fadenAnzeige;
    }

    @Override 
    public TabellenmodellMitarbeiter doInBackground()
    {       
        this.anzeige.setProgress(0);
        this.anzeige.setNote("1.) Datenabfrage aufrufen ...");

        TabellenmodellMitarbeiter tm = new TabellenmodellMitarbeiter();     
        String daten = null;

        try 
        {   
            URL url = new URL("http://www.greif-integra.de/daten/php/mitarbeiter/select_mitarbeiter_tabelle.php");                  
            PhpPostConnect con = new PhpPostConnect(url);

            this.anzeige.setProgress(30);
            this.anzeige.setNote("2.) Daten lesen ...");

            try
            {
                daten = con.read();

                this.anzeige.setProgress(60);
                this.anzeige.setNote("3.) Daten aufbereiten ...");

                // here the received data is being processed
            }
            catch (IOException e)
            {
                meldungTabelle.setText("FEHLER Die Tabelle kann nicht angezeigt werden. IOException");
            }
        }
        catch (MalformedURLException e)
        {
            meldungTabelle.setText("FEHLER Die Tabelle kann nicht angezeigt werden. MalformedURLException");
        }
        catch (Exception e)
        {
            meldungTabelle.setText("FEHLER Die Tabelle kann nicht angezeigt werden. Exception");
        }

        this.anzeige.setProgress(90);
        this.anzeige.setNote("4.) Die Tabelle erzeugen ...");

        return tm;
    }

    @Override protected void done()
    {       
            // some work with the data is done here

        this.fadenAnzeige.cancel(true);
        this.anzeige.close();
    }
}

在我的Java程序中,我启动并执行第二个类的对象。

    class ErstelleTabellenmodellMitAnzeige extends SwingWorker<Object, Object>
{       
    @Override
    protected Object doInBackground()
    {
        ProgressMonitor anzeige = new ProgressMonitor(KarteMitarbeiter.this,
                                                      "Fortschrittsanzeige",
                                                      "",
                                                      0,
                                                      100);

        ArbeiterErstelleTabellenmodell fadenTabellenmodell = new ArbeiterErstelleTabellenmodell(anzeige, this);
        fadenTabellenmodell.execute();

        while(true)
        {
            try
            {
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {}

            if(anzeige.isCanceled())
            {
                fadenTabellenmodell.cancel(true);
                break;
            }
        }

        anzeige.close();
        return null;
    }
}

也许没有最佳解决方案。我只想确保,因为我想每天使用该软件。先感谢您。我很感激你的想法。

3 个答案:

答案 0 :(得分:0)

  

因此两个线程可以相互抵消。但这是最佳解决方案吗?是否有更好,更安全的方法来做到这一点?

最佳解决方案是设置一个常规检查的volatile标志,以查看该线程是否应该停止运行。如果您在Stream上阻塞,则可以关闭该流并使其触发IOException(如Channels的AsynchronousCloseException)


如果你真的别无选择,你可以使用thread.stop();这将导致线程抛出ThreadDeath错误,该错误应触发它以展开堆栈(和任何锁定)并导致你的线程死。这应仅用作最后一个资源,它可能使数据处于不一致状态。即错误可能会被抛出任何一行。

注意:如果你捕获Throwable,或者Error或ThreadDeath,这将捕获此错误,就像任何其他错误一样,线程也不会死。

答案 1 :(得分:0)

只要您收到用户点击“取消”的通知,就可以删除一个线程。我使用答案herehere中的代码制作了一个工作示例。
您需要下载SwingUtils课程才能使示例正常工作。

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicReference;

import javax.accessibility.AccessibleContext;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;

public class Q22126862 {

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            new SwingWorkerExample();
        }
    });
}

static class SwingWorkerExample extends JFrame implements ActionListener {

    private static final long serialVersionUID = 1L;
    private final JButton startButton;
    private MySwingWorker swingWorker;

    public SwingWorkerExample() {
        super("SwingWorkerExample");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setLayout(new GridLayout(2, 2));
        startButton = makeButton("Start");
        //Display the window.
        pack();
        setVisible(true);
    }       

    private JButton makeButton(String caption) {

        JButton b = new JButton(caption);
        b.setActionCommand(caption);
        b.addActionListener(this);
        getContentPane().add(b);
        return b;
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if ("Start".equals(e.getActionCommand())) {
            startButton.setEnabled(false);
            // Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one.
            ProgressMonitor progressMonitor = new ProgressMonitor(this, "Sleep progress", "sleeping", 0, 99);
            (swingWorker = new MySwingWorker(this, progressMonitor, 3000, 10)).execute(); // new instance
        } else if ("TaskDone".equals(e.getActionCommand())) {
            startButton.setEnabled(true);
            System.out.println("SwingWorker task finished OK: " + swingWorker.getResult());
        } else {
            System.out.println("Unknown action: " + e);
        }
    }
}

static class MySwingWorker extends javax.swing.SwingWorker<Boolean, Void> implements ActionListener {

    private final ActionListener taskListener;
    private final long sleepMs; 
    private final int sleepSteps;
    private final ProgressMonitor progressMonitor;
    private final AtomicReference<Thread> currentThread = new AtomicReference<Thread>();
    private volatile boolean done;
    private JButton cancelButton;
    private boolean result;

    public MySwingWorker(ActionListener taskListener, ProgressMonitor progressMonitor, long sleepMs, int sleepSteps) {
        super();
        this.taskListener = taskListener;
        this.sleepMs = sleepMs;
        this.sleepSteps = sleepSteps;
        this.progressMonitor = progressMonitor;
    }

    @Override
    protected Boolean doInBackground() {

        currentThread.set(Thread.currentThread());
        long sleepTimeMs = sleepMs / sleepSteps; 
        try {

            // Initialize the progress monitor so that it has a backing JDialog
            progressMonitor.setMillisToDecideToPopup(0);
            progressMonitor.setProgress(0);
            AccessibleContext ac = progressMonitor.getAccessibleContext();
            JDialog dialog = (JDialog)ac.getAccessibleParent();
            java.util.List<JButton> components = darrylbu.util.SwingUtils.getDescendantsOfType(JButton.class, dialog, true);
            cancelButton = components.get(0);
            cancelButton.addActionListener(this);

            for (int i = 0; i < sleepSteps; i++) {
                Thread.sleep(sleepTimeMs);
                int progress = (int)((i / ((float)sleepSteps)) * 100.0);
                progressMonitor.setProgress(progress);
                System.out.println("Sleep progress: " + progress);
            }
            result = true;

        } catch (Exception e) {
            //e.printStackTrace();
            System.out.println(e.toString());
        } finally {
            done = true;
            currentThread.set(null);
            System.out.println("Background task done");
        }
        return result;
    }

    public Boolean getResult() {
        return result;
    }

    @Override
    protected void done() {

        System.out.println("Task done");
        progressMonitor.close();
        System.out.println("Monitor closed");
        ActionEvent e = new ActionEvent(this, 0, "TaskDone");
        taskListener.actionPerformed(e);
    }

    protected void cancel() {

        if (done) {
            return;
        }
        Thread t = currentThread.get();
        if (t != null) {
            t.interrupt();
        }
        // In case if I/O-tasks, close the source that is read from (e.g. socket).
        // Interrupting a blocked reading thread has no effect in this case. 
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        System.out.println("Action: " + e);
        if ("Cancel".equals(e.getActionCommand())) {
            cancel();
        }
    }
}

}

答案 2 :(得分:0)

Marko Topolnik让我走上正轨:

  

我认为两个工作线程没有任何改进。您的   根本问题是使用不可中断的阻塞I / O.   操作。 - Marko Topolnik

必须更改阻止I / O.

我正在使用URLConnection,现在我发现可以做

    .setConnectTimeout(1500);
    .setReadTimeout(1800);

现在我不需要停止WorkerThread,因为IO Operation会在WorkerThread卡住时抛出异常。

无论如何,在我找到这个简单的解决方案之前,我通过使用带有timeout-parameter的get()来从WorkerThread检索结果。这会抛出一个TimeoutException,可用于对WorkerThread执行cancel()。 WorkerThread必须检查if(cancel())返回;停止自己