4

netbeans Swing GUI を使用しているスレッドに問題があります。Java の File System Notifier を使用してバックアップ プログラムの GUI を実際に開発しようとするのはこれが初めてです。私は2つのファイルSyncUI.javaSync.java.

私がやりたいことのほとんどは、jTextField1テキスト フィールドにディレクトリ パスを入力して、新しい同期オブジェクトを作成し、そのオブジェクトを呼び出す同期スレッドを作成するprocessEventsことです。そのディレクトリ内のファイルが変更されたときに、変更に関するテキストをリストに追加したいと考えています。

現在の状態では、UI は応答しなくなりましたprocessEventsが、リストに何も追加されていません。問題は何ですか?また、Java を使い始めたばかりなので、建設的な批判を歓迎します。

SyncUI.java:

package sync;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.SwingUtilities;

public class SyncUI extends javax.swing.JFrame {

    public SyncUI() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {
        jButton1 = new javax.swing.JButton();
        jTextField1 = new javax.swing.JTextField();
        list1 = new java.awt.List();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Start");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jTextField1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextField1ActionPerformed(evt);
            }
        });

        list1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                list1ActionPerformed(evt);
            }
        });

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
        .add(layout.createSequentialGroup()
        .addContainerGap()
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
        .add(list1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .add(layout.createSequentialGroup()
        .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 329, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
        .add(jButton1)
        .add(0, 0, Short.MAX_VALUE)))
        .addContainerGap())
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
        .add(layout.createSequentialGroup()
        .addContainerGap()
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
        .add(jButton1)
        .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
        .add(list1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 229, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
        .addContainerGap(25, Short.MAX_VALUE))
    );

        pack();
    }// </editor-fold>                        

private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {                                            
    jButton1.doClick();
}                                           

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {  
        //I tried to use invokeLater, this solved the problem of the UI nonresponse issue                                       
        SwingUtilities.invokeLater(new Runnable() {
            public void run()
            {
                //Creates a path dir that my Sync constructor needs to start file   notification watcher on that directory
                Path dir = Paths.get(jTextField1.getText());
                try
                {
                    //Creates a new sync object passing it the directory and the list in my GUI
                    Sync sync = new Sync(dir, list1);
                    try
                    {
                        sync.processEvents();
                    }
                    catch (InterruptedException ex)
                    {
                        Logger.getLogger(SyncUI.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                catch (IOException ex)
                {
                    Logger.getLogger(SyncUI.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        });
}                                        

private void list1ActionPerformed(java.awt.event.ActionEvent evt) {                                      
    // TODO add your handling code here:
}                                     

public static void main(String args[]) throws IOException
{
    java.awt.EventQueue.invokeLater(new Runnable()
    {
        public void run()
        {
            SyncUI s = new SyncUI();
            s.setVisible(true);               
        }
    });     
}

// Variables declaration - do not modify                     
private javax.swing.JButton jButton1;
private javax.swing.JTextField jTextField1;
private java.awt.List list1;
// End of variables declaration                   
}

同期 Java:

package sync;

import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;

public class Sync
{
    private final WatchService ws;
    private final Map<WatchKey,Path> keys;
    public java.awt.List list;

    public Sync(Path dir, java.awt.List list) throws IOException, InterruptedException
    {
        this.ws = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey,Path>();
        this.list = list;
        recSet(dir);
        //this.processEvents();
    }

    private void register(Path dir) throws IOException
    {
        WatchKey key = dir.register(ws, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        keys.put(key, dir);
    }

    private void recSet(Path start) throws IOException
    {
        Files.walkFileTree(start, new SimpleFileVisitor<Path>()
        {     
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
            {
                if(!Files.isHidden(dir))
                {
                    register(dir);
                    System.out.println(dir);
                }
            return FileVisitResult.CONTINUE;
            }
        });
    }

    void processEvents() throws IOException, InterruptedException
    {
        System.out.println("Entered processEvents");
        SwingUtilities.invokeLater(new Runnable() {
            public void run()
            {
                System.out.println("Entered run");
                list.add("test2");
                list.repaint();
                while(true)
                { 
                    WatchKey key;           
                    try
                    {
                        key = ws.take();
                    }
                    catch (InterruptedException x)
                    {
                        return;
                    }

                    Path dir = keys.get(key);
                    if (dir == null)
                    {
                        System.err.println("WatchKey not recognized");
                        continue;
                    }

                    for (WatchEvent<?> event: key.pollEvents())
                    {
                        WatchEvent.Kind<?> kind = event.kind();
                        WatchEvent<Path> ev = (WatchEvent<Path>)event;
                        Path filename = ev.context();
                        String name = dir.resolve(filename).toString();             

                        if (kind == OVERFLOW)
                            continue;               

                        if(kind == ENTRY_CREATE)
                        {
                            System.out.print("Entry Created: ");
                            File f = new File(name);

                            if(f.isDirectory())
                                try {
                                    register(dir.resolve(filename));
                                } catch (IOException ex) {
                                    Logger.getLogger(Sync.class.getName()).log(Level.SEVERE, null, ex);
                                }

                            System.out.println(name);
                            list.add(name);     
                        }
                        else if(kind == ENTRY_DELETE)
                        {
                            System.out.print("Entry Deleted: ");
                            System.out.println(name);                          
                        }
                        else if(kind == ENTRY_MODIFY)
                        {
                            File f = new File(name);
                            if(!f.isDirectory())
                        {
                            System.out.print("Entry Modify: ");
                            System.out.println(name);
                        }
                    }

                    boolean valid = key.reset();

                    if (!valid)
                        break;
                    }
                }
            }
        });       
    }
}
4

3 に答える 3

7

Swing はスレッド セーフではないため、同じスレッドで UI を更新しようとすると、「アプリケーション フリーズ」動作が発生します。これを解決するには、UI の更新プロセスを別のスレッドに委譲する必要があります。これは、SwingUtilities.invokeLater (Java 5 以前) メソッドおよび/または SwingWorker クラス (Java 6 以降) を使用して作成されます。

いくつかのリンク:

Google 検索: https://www.google.com.br/search?q=swing+thread+safe

于 2012-07-20T04:31:39.263 に答える
4

davidbuzatto を支持して:

いいえ、私はあなたが理解InvokeLaterしていないと思います。 InvokeLaterランナブルが で実行されることを保証しETDます。つまり、基本的に、私が読むことができることから、長時間実行されているイベント ブロッキング コードをETD. InvokeLaterUIを更新したいSwingWorkerときやスレッドを利用したいとき、実際に処理を行いたいときだけ利用する

void processEvents() throws IOException, InterruptedException
    {
        System.out.println("Entered processEvents");

        // PLEASE ETD, PUT THIS AT THE END OF THE QUEUE AND EXECUTE
        // SO I RUN WITHIN YOUR CONTEXT
        SwingUtilities.invokeLater(new Runnable() {
            public void run()
            {

                // NOW RUNNING BACK ON THE ETD
                System.out.println("Entered run");
                list.add("test2");
                list.repaint();

                // NOW BLOCK THE ETD, SO NO MORE REPAINTS OR UPDATES WILL EVER
                // OCCUR
                while(true)
                { 
                    WatchKey key;           
                    try
                    {
                        key = ws.take();
                    }
                    catch (InterruptedException x)
                    {
                        return;
                    }

大文字で申し訳ありませんが、コメントを目立たせたかったのです。

于 2012-07-20T04:54:22.103 に答える
2

1. Swing はThread safe ではありませんrepaint(), setText()が、Tread Safeのようなメソッドもあります。

2. Swingのmain()メソッドはNot Long Livedです。イベント ディスパッチャ スレッドで GUI の構築をスケジュールし、終了します。これで、GUI を処理するのは EDT の責任になります。

3.非 UI 作業は、非 UI スレッドで維持する必要があります。GUI スレッド、つまり EDT から切り離してください。

4.メソッドは、 JFramemain()を可視化する作業のみを行う必要があります。EventQueue.invokeLater.

例えば:

    public static void main(String[] args){

       EventQueue.invokeLater(new Runnable(){

       public void run(){

       myFrame.setVisible(true);
     }
  }
}

5 SwingWorker は Java によって提供され、GUI スレッドで非 UI スレッドの作業出力を同期します。

于 2012-07-20T05:03:56.343 に答える