マルチスレッドとswing
JAva swingのほとんどのクラスはスレッドセキュリティではありません.複数のスレッドでswingオブジェクトを操作すると、多くの奇妙な現象が発生する可能性があります.スレッドを安全にしたい場合は、特殊なスレッドでswingオブジェクト、つまりEDTスレッド、つまりイベントスケジューリングスレッド(Event Dispatch Thread,EDT)を操作する必要があります.
1つのGUIプログラムは多くのスレッドが同時に実行され、そのうち1つはイベントスケジューリングスレッド(EDT)と呼ばれ、プログラム内のすべてのコールバック(最も一般的なのはボタンをクリックして実行するactionPerformed方法)を処理するために使用され、すべての操作Swingオブジェクトの操作はこのスレッドの中に置かなければならない.そうしないと問題が発生する.
例を見て
notInEDTで他のスレッドでTextの値を変更すると、すぐに問題が発生し、異常は放出されないはずです.
次のようにEDTスレッドで処理すれば問題ありません
ここでinvokeLaterメソッドを呼び出してUI操作をEDTスレッドに捨てて処理し、説明を見てみましょう.
RunnableインタフェースのrunメソッドをEDTスレッド内で非同期で実行できるようにするのは、この操作をキューキューの最後にして、前の操作が完了してからこの操作を実行することに等しい.
1つのGUIプログラムは多くのスレッドが同時に実行され、そのうち1つはイベントスケジューリングスレッド(EDT)と呼ばれ、プログラム内のすべてのコールバック(最も一般的なのはボタンをクリックして実行するactionPerformed方法)を処理するために使用され、すべての操作Swingオブジェクトの操作はこのスレッドの中に置かなければならない.そうしないと問題が発生する.
例を見て
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* NotInEDTSample
just demonstrates the usage of Swing EDT simply.
*
* @author Jimmy.haung(SZ Team)
* @since DUI (Mar 25, 2013)
*/
public class NotInEDTSample extends JFrame {
private static final long serialVersionUID = 1L;
private JTextField m_txt;
public NotInEDTSample() {
initGUI();
notInEDT();
}
/**
* Init the GUI
*/
public void initGUI() {
this.setTitle("a simple EDT Sample");
m_txt = new JTextField();
getContentPane().add(m_txt, BorderLayout.CENTER);
}
/**
* Process not under the EDT. 10 m_txt
.
*/
private void notInEDT() {
for (int i = 0; i < 4; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
m_txt.setText(" EDT !");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
/*private void notInEDT() {
for (int i = 0; i < 4; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
m_txt.setText(" EDT !");
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}*/
/**
* Launch the application.
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
NotInEDTSample oFrame = new NotInEDTSample();
oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
oFrame.setLocationRelativeTo(null);
oFrame.setSize(300, 200);
oFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
notInEDTで他のスレッドでTextの値を変更すると、すぐに問題が発生し、異常は放出されないはずです.
次のようにEDTスレッドで処理すれば問題ありません
private void notInEDT() {
for (int i = 0; i < 4; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
m_txt.setText(" EDT !");
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
ここでinvokeLaterメソッドを呼び出してUI操作をEDTスレッドに捨てて処理し、説明を見てみましょう.
RunnableインタフェースのrunメソッドをEDTスレッド内で非同期で実行できるようにするのは、この操作をキューキューの最後にして、前の操作が完了してからこの操作を実行することに等しい.
/**
* Causes doRun.run() to be executed asynchronously on the
* AWT event dispatching thread. This will happen after all
* pending AWT events have been processed. This method should
* be used when an application thread needs to update the GUI.
* In the following example the invokeLater
call queues
* the Runnable
object doHelloWorld
* on the event dispatching thread and
* then prints a message.
*
* Runnable doHelloWorld = new Runnable() {
* public void run() {
* System.out.println("Hello World on " + Thread.currentThread());
* }
* };
*
* SwingUtilities.invokeLater(doHelloWorld);
* System.out.println("This might well be displayed before the other message.");
*
* If invokeLater is called from the event dispatching thread --
* for example, from a JButton's ActionListener -- the doRun.run() will
* still be deferred until all pending events have been processed.
* Note that if the doRun.run() throws an uncaught exception
* the event dispatching thread will unwind (not the current thread).
*
* Additional documentation and examples for this method can be
* found in
* How to Use Threads ,
* in The Java Tutorial.
*
* As of 1.3 this method is just a cover for java.awt.EventQueue.invokeLater()
.
*
* Unlike the rest of Swing, this method can be invoked from any thread.
*
* @see #invokeAndWait
*/
public static void invokeLater(Runnable doRun)
は ていますが、 いがある があります.invokeAndWait 、 とinvokeLaterの は くありません.run をEDTに れて することもできますが、 いは であり、EDTで び すことはできません./**
* Causes doRun.run()
to be executed synchronously on the
* AWT event dispatching thread. This call blocks until
* all pending AWT events have been processed and (then)
* doRun.run()
returns. This method should
* be used when an application thread needs to update the GUI.
* It shouldn't be called from the event dispatching thread.
* Here's an example that creates a new application thread
* that uses invokeAndWait
to print a string from the event
* dispatching thread and then, when that's finished, print
* a string from the application thread.
*
* final Runnable doHelloWorld = new Runnable() {
* public void run() {
* System.out.println("Hello World on " + Thread.currentThread());
* }
* };
*
* Thread appThread = new Thread() {
* public void run() {
* try {
* SwingUtilities.invokeAndWait(doHelloWorld);
* }
* catch (Exception e) {
* e.printStackTrace();
* }
* System.out.println("Finished on " + Thread.currentThread());
* }
* };
* appThread.start();
*
* Note that if the Runnable.run
method throws an
* uncaught exception
* (on the event dispatching thread) it's caught and rethrown, as
* an InvocationTargetException
, on the caller's thread.
*
* Additional documentation and examples for this method can be
* found in
* How to Use Threads ,
* in The Java Tutorial.
*
* As of 1.3 this method is just a cover for
* java.awt.EventQueue.invokeAndWait()
.
*
* @exception InterruptedException if we're interrupted while waiting for
* the event dispatching thread to finish excecuting
* doRun.run()
* @exception InvocationTargetException if an exception is thrown
* while running doRun
*
* @see #invokeLater
*/
public static void invokeAndWait(final Runnable doRun)
throws InterruptedException, InvocationTargetException
に、EDTでinvokeAndWaitを び すことができない を に します.
まずjava swingのeventキューを する があります.UIの なすべての に してeventを し、eventキューに します.EDTスレッドで つ つ します. えば、マウスでボタンをクリックすると、ボタンに されているイベントをeventに して します.
invokeLaterとinvokeAndWaitのソースコードの いを します/**
* invokeLater runnable eventQueue
*/
public static void invokeLater(Runnable runnable) {
Toolkit.getEventQueue().postEvent(
new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
}
static void invokeAndWait(Object source, Runnable runnable)
throws InterruptedException, InvocationTargetException
{
if (EventQueue.isDispatchThread()) {// EDT
throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
}
class AWTInvocationLock {}
Object lock = new AWTInvocationLock();
InvocationEvent event =
new InvocationEvent(source, runnable, lock, true);
// , , wait
synchronized (lock) {
Toolkit.getEventQueue().postEvent(event);
lock.wait();
}
Throwable eventThrowable = event.getThrowable();
if (eventThrowable != null) {
throw new InvocationTargetException(eventThrowable);
}
}
invokeAndWaitの waitして、それからどこがnotifyなのか て、InvocationEventの で/**
* Executes the Runnable's run()
method and notifies the
* notifier (if any) when run()
has returned or thrown an exception.
*
* @see #isDispatched
*/
public void dispatch() {
try {
if (catchExceptions) {
try {
runnable.run();
}
catch (Throwable t) {
if (t instanceof Exception) {
exception = (Exception) t;
}
throwable = t;
}
}
else {
runnable.run();
}
} finally {
dispatched = true;
if (notifier != null) {
synchronized (notifier) {
notifier.notifyAll();// ,notify
}
}
}
}
これはinvokeAndWaitという が の であり,この を び すスレッドが に まって が したことを っているからである.
EDTでinvokeAndWaitを び すと、 ずデッドロックになります. の を てみましょう.import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestAction extends JFrame {
private static final long serialVersionUID = -7462155330900531124L;
private JButton jb1 = new JButton(" ");
private JTextField txt = new JTextField(10);
public TestAction() {
jb1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String name = ((JButton) e.getSource()).getText();
txt.setText(name);
}
});
setLayout(null);
add(txt);
add(jb1);
txt.setBounds(50, 100, 200, 30);
jb1.setBounds(270, 100, 70, 30);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SwingConsole.run(new TestAction(), 500, 500);
}
});
}
}
import javax.swing.*;
public class SwingConsole {
public static void run(final JFrame f, final int width, final int height) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setTitle(f.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(width, height);
f.setVisible(true);
}
});
}
}
このように くのは いありません~~もし たちがinvokeAndWaitを うならば、きっとデッドロックになりますimport java.lang.reflect.InvocationTargetException;
import javax.swing.*;
public class SwingConsole {
public static void run(final JFrame f, final int width, final int height){
try {
SwingUtilities.invokeAndWait(new Runnable(){
public void run(){
f.setTitle(f.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(width, height);
f.setVisible(true);
}
});
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
この にはもう つのポイントがあります.EDTで のかかる を び さないでください.そうすると、インタフェースカードの になります.
1つの では、10ファイルの アップロードをシミュレートし、 バーを します.import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TestProgress extends Thread implements ActionListener {
private static JProgressBar progressBar;
JFrame jf = new JFrame("Test");
JPanel jp = new JPanel();
JTextArea jta = new JTextArea();
JButton jb = new JButton(" ");
public static void main(String[] args) {
new TestProgress();
}
public TestProgress() {
jp.setLayout(new FlowLayout());
progressBar = new JProgressBar();
progressBar.setValue(0);
progressBar.setStringPainted(true);
jf.add(jp, BorderLayout.NORTH);
jf.add(new JScrollPane(jta));
jp.add(progressBar);
jp.add(jb);
jf.add(new JScrollPane(jta), BorderLayout.CENTER);
jb.addActionListener(this);
jf.setSize(300, 200);
jf.setLocation(300, 200);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void run() {
for (int i = 0; i < 10;i++) {//10
UpLordTread lordTread = new UpLordTread(progressBar,jta," " + i);
lordTread.start();//
try {
lordTread.join();// ~~
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void actionPerformed(ActionEvent e) {
String comm = e.getActionCommand();
if (" ".equals(comm)) {
this.start();// EDT , UI
jb.setEnabled(false);
}
}
}
/**
*
* @author yellowbaby
*
*/
class UpLordTread extends Thread{
JTextArea jta;
JProgressBar progressBar;
public UpLordTread(JProgressBar progressBar,JTextArea jta,String fileName) {
super(fileName);
this.jta = jta;
this.progressBar = progressBar;
}
public void run() {
for (int i = 0; i <= 100; i++) {
progressBar.setValue(i);
String temp = Thread.currentThread().getName() + ":" + i + "
";
jta.append(temp);
try {
Thread.sleep(10);
} catch (Exception ee) {
ee.printStackTrace();
}
}
progressBar.setValue(0);
}
}
たちがボタンをクリックした 、actionPerformaedでアップロードしたループコードを び すのではなく、 しいスレッドを いて しました.これはなぜですか.
actionPerformaedのコードはEDTによって び されるので、この がすぐに さなければ、EDTスレッドは のイベントを することができず、インタフェースも まってしまいます.
このようにコードを してみると、ボタンをクリックすると が まってしまい、すべてアップロードしてから が されます.import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TestProgress extends Thread implements ActionListener {
private static JProgressBar progressBar;
JFrame jf = new JFrame("Test");
JPanel jp = new JPanel();
JTextArea jta = new JTextArea();
JButton jb = new JButton(" ");
public static void main(String[] args) {
new TestProgress();
}
public TestProgress() {
jp.setLayout(new FlowLayout());
progressBar = new JProgressBar();
progressBar.setValue(0);
progressBar.setStringPainted(true);
jf.add(jp, BorderLayout.NORTH);
jf.add(new JScrollPane(jta));
jp.add(progressBar);
jp.add(jb);
jf.add(new JScrollPane(jta), BorderLayout.CENTER);
jb.addActionListener(this);
jf.setSize(300, 200);
jf.setLocation(300, 200);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void run() {
for (int i = 0; i < 10;i++) {//10
UpLordTread lordTread = new UpLordTread(progressBar,jta," " + i);
lordTread.start();//
try {
lordTread.join();// ~~
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void actionPerformed(ActionEvent e) {
String comm = e.getActionCommand();
if (" ".equals(comm)) {
//this.start();// EDT , UI
for (int i = 0; i < 10;i++) {//10
UpLordTread lordTread = new UpLordTread(progressBar,jta," " + i);
lordTread.start();//
try {
lordTread.join();// ~~
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
jb.setEnabled(false);
}
}
}
/**
*
* @author yellowbaby
*
*/
class UpLordTread extends Thread{
JTextArea jta;
JProgressBar progressBar;
public UpLordTread(JProgressBar progressBar,JTextArea jta,String fileName) {
super(fileName);
this.jta = jta;
this.progressBar = progressBar;
}
public void run() {
for (int i = 0; i <= 100; i++) {
progressBar.setValue(i);
String temp = Thread.currentThread().getName() + ":" + i + "
";
jta.append(temp);
try {
Thread.sleep(10);
} catch (Exception ee) {
ee.printStackTrace();
}
}
progressBar.setValue(0);
}
}
だから たちの のかかる はすべて のスレッドに てて しなければならなくて、JDKの にSwingWorkが たちにこの を することができますimport java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TestProgress extends Thread implements ActionListener {
private static JProgressBar progressBar;
JFrame jf = new JFrame("Test");
JPanel jp = new JPanel();
JTextArea jta = new JTextArea();
JButton jb = new JButton(" ");
public static void main(String[] args) {
new TestProgress();
}
public TestProgress() {
jp.setLayout(new FlowLayout());
progressBar = new JProgressBar();
progressBar.setValue(0);
progressBar.setStringPainted(true);
jf.add(jp, BorderLayout.NORTH);
jf.add(new JScrollPane(jta));
jp.add(progressBar);
jp.add(jb);
jf.add(new JScrollPane(jta), BorderLayout.CENTER);
jb.addActionListener(this);
jf.setSize(300, 200);
jf.setLocation(300, 200);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
String comm = e.getActionCommand();
if (" ".equals(comm)) {
SwingWorker swingWorker = new SwingWorker() {
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < 10; i++) {// 10
UpLordTread lordTread = new UpLordTread(progressBar,
jta, " " + i);
lordTread.start();//
try {
lordTread.join();// ~~
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void done() {
System.out.println(" ");
}
};
swingWorker.execute();
jb.setEnabled(false);
}
}
}
/**
*
*
* @author yellowbaby
*
*/
class UpLordTread extends Thread {
JTextArea jta;
JProgressBar progressBar;
public UpLordTread(JProgressBar progressBar, JTextArea jta, String fileName) {
super(fileName);
this.jta = jta;
this.progressBar = progressBar;
}
public void run() {
for (int i = 0; i <= 100; i++) {
progressBar.setValue(i);
String temp = Thread.currentThread().getName() + ":" + i + "
";
jta.append(temp);
try {
Thread.sleep(10);
} catch (Exception ee) {
ee.printStackTrace();
}
}
progressBar.setValue(0);
}
}
SwingWorkには2つの な があります.doInBackgroundとdone、doInBackgroundは たちのバックグラウンドスレッドです.doneはdoInBackgroundを した に び され、 にUIを するために されます.