マルチスレッドとswing


JAva swingのほとんどのクラスはスレッドセキュリティではありません.複数のスレッドでswingオブジェクトを操作すると、多くの奇妙な現象が発生する可能性があります.スレッドを安全にしたい場合は、特殊なスレッドでswingオブジェクト、つまりEDTスレッド、つまりイベントスケジューリングスレッド(Event Dispatch Thread,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を するために されます.