我阅读了很多关于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;
}
}
也许没有最佳解决方案。我只想确保,因为我想每天使用该软件。先感谢您。我很感激你的想法。
答案 0 :(得分:0)
因此两个线程可以相互抵消。但这是最佳解决方案吗?是否有更好,更安全的方法来做到这一点?
最佳解决方案是设置一个常规检查的volatile标志,以查看该线程是否应该停止运行。如果您在Stream上阻塞,则可以关闭该流并使其触发IOException(如Channels的AsynchronousCloseException)
如果你真的别无选择,你可以使用thread.stop();
这将导致线程抛出ThreadDeath
错误,该错误应触发它以展开堆栈(和任何锁定)并导致你的线程死。这应仅用作最后一个资源,它可能使数据处于不一致状态。即错误可能会被抛出任何一行。
注意:如果你捕获Throwable,或者Error或ThreadDeath,这将捕获此错误,就像任何其他错误一样,线程也不会死。
答案 1 :(得分:0)
只要您收到用户点击“取消”的通知,就可以删除一个线程。我使用答案here和here中的代码制作了一个工作示例。
您需要下载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())返回;停止自己