[英]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必須檢查是否返回if(canceled()); 阻止自己。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.