簡體   English   中英

凍結線程后,如何從外部停止Java中的線程?

[英]How to stop a Thread in Java from the outside when the Thread is frozen?

我讀了很多有關Java線程的文章,但是我不確定最佳解決方案。

我創建了一個工作線程來訪問php腳本(並且php腳本訪問mysql數據庫)。 如果以某種方式使用php-Script或mysql-database的服務器繁忙,則線程在讀取或發送操作中中斷。 因此,設置中斷並讓Thread本身停止的概念不起作用。

現在,我使用ProgressMonitor創建了第二個工作線程。 當用戶單擊ProgressMonitor的“取消”按鈕時,凍結的第一個線程將被取消。 如果第一個線程正常工作,它將取消第二個線程。 因此,兩個線程可以互相抵消。

但這是最佳解決方案嗎? 有沒有更好,更安全的方法呢?

    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;
    }
}

也許沒有最佳解決方案。 我只想確定一下,因為我想每天使用該軟件。 先感謝您。 感謝您的想法。

因此,兩個線程可以互相抵消。 但這是最佳解決方案嗎? 有沒有更好,更安全的方法呢?

最好的解決方案是設置一個volatile標志,該標志將定期檢查以查看線程是否應該停止運行。 如果在Stream上進行阻止,則可以關閉該流並使其觸發IOException(如Channels的AsynchronousCloseException)


如果您真的別無選擇,可以使用thread.stop(); 這將導致線程拋出ThreadDeath錯誤,這將觸發該線程ThreadDeath堆棧(和所有鎖)並導致線程死亡。 這只能用作最后一個資源,它可能會使數據處於不一致狀態。 即錯誤可以拋出在任何行上。

注意:如果捕獲Throwable或Error或ThreadDeath,則將捕獲此錯誤,就像其他任何線程一樣,該線程也不會死亡。

只要通知您用戶單擊“取消”,您就可以擺脫一個線程。 我使用此處此處的答案中的代碼制作了一個工作示例。
您將需要下載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();
        }
    }
}

}

Marko Topolnik使我走上了正確的軌道:

我看不到有兩個工作線程會有任何改善。 您的基本問題是使用不間斷的阻塞I / O操作。 – Marko Topolnik

阻塞I / O必須更改。

我正在使用URLConnection,現在發現可以執行此操作

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

現在,我不需要停止WorkerThread,因為IO操作卡住時,它將在WorkerThread上引發異常。

無論如何,在找到這個簡單的解決方案之前,我通過使用帶有超時參數的get()來從WorkerThread中檢索結果來解決了問題。 這將引發一個TimeoutException,可用於對WorkerThread進行cancel()。 WorkerThread必須檢查是否返回i​​f(canceled()); 阻止自己。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM