0

JavaSwingログインパネルがあります。ユーザーが「ログイン」ボタンをクリックすると、広範なDB通信が行われます。そのdb通信を別のスレッドに入れるというアイデアを思いつきました(db通信はいくつかのハッシュマップとシングルトンを埋めるために使用されます)。その通信を別のスレッドに入れた後、ユーザーがunameとパスワードを入力しているときにdbの重労働が発生します。ユーザーが「ログイン」ボタンをクリックすると、コードは「重労働」スレッドが参加するのを待ってから続行します。

問題は、コードがdbスレッドの参加を待機している間、UIの更新を実行できないように見えることです。SWが終了する前でも、ユーザーはいつでも「ログイン」ボタンをクリックでき、SwingWorkerに参加する方法がないため、db通信にSwingWorkerを使用できるとは思いません(db通信は実際のログインの前に行う必要があります) 。

別のスレッドが参加するのを待っている間にSwingUIの更新を有効にする方法はありますか?私は何かが足りないのですか?

4

5 に答える 5

3

単純に 2 つのフラグを設定し、両方の操作 (DB 通信とユーザー クリック) の最後に同じメソッドを呼び出して、2 つのフラグの状態を確認します。ユーザー入力をブロックしたい場合は、モーダル ダイアログにプログレス バーを表示できます。

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UnsupportedLookAndFeelException;

public class TestThreadJoin {

    private boolean dbWorkDone = false;
    private boolean credentialsProvided = false;
    private JTextField loginTF;
    private JTextField passwordTF;

    protected void initUI() {
        final JFrame frame = new JFrame(TestThreadJoin.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridBagLayout());
        JLabel login = new JLabel("Login: ");
        JLabel password = new JLabel("Password: ");
        loginTF = new JTextField(20);
        passwordTF = new JPasswordField(20);
        GridBagConstraints gbc = new GridBagConstraints();
        panel.add(login, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        panel.add(loginTF, gbc);
        gbc.gridwidth = 1;
        panel.add(password, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        panel.add(passwordTF, gbc);
        JButton loginButton = new JButton("Login");
        loginButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                credentialsProvided = true;
                proceed();
            }
        });
        frame.add(panel);
        frame.add(loginButton, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

            @Override
            protected Void doInBackground() throws Exception {
                Thread.sleep(10000);
                return null;
            }

            @Override
            protected void done() {
                super.done();
                dbWorkDone = true;
                proceed();
            }
        };
        worker.execute();
    }

    protected void proceed() {
        if (credentialsProvided && dbWorkDone) {
            System.err.println("Continuing ");
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestThreadJoin().initUI();
            }
        });
    }
}
于 2013-01-11T11:03:46.867 に答える
2

SwingWorker を使用して実行できます。この ActionListener を JButton に追加します。また、'label' の JLabel フィールドと 'progressbar' の JProgressBar フィールドを提供します。

java.awt.event.ActionListener al = new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
        SwingWorker swingWorker = new SwingWorker() {

            @Override
            protected Object doInBackground() throws Exception {

                // SIMULATE DB WORK
                for (int i = 0; i < 10; i++) {
                    synchronized (this) {
                        this.wait(300);
                    }
                    publish(10 * i);
                }
                publish(100);

                return 0;
            }

            @Override
            protected void done() {
                label.setText("Work complete");
                button.setEnable(true);
            }

            @Override
            protected void process(List chunks) {
                for (Object object : chunks) {
                    Integer progress = (Integer) object;
                    progressBar.setValue(progress);
                }
            }

        };

        button.setEnable(false);
        swingWorker.execute();
    }
}

DB 作業は定量化 (測定) できない可能性があるため、次の変更を行います。

  • 「swingWorker.execute()」行の上に「progressbar.setIndeterminate(true)」を挿入し、
  • 「label.setText()」行の上に「progressbar.setIndeterminate(false)」を挿入し、
  • 「publish(int)」呼び出しを削除します。
于 2013-01-11T10:44:25.183 に答える
2

次のようなことを行って、SwingWorker を試してみるべきだと思います。

  • ユーザーがログインをクリックすると、swing ワーカーが開始されます (new Swingworker<...,...>().execute();)
  • doInBackground()で、最初にpusblih()を呼び出してprocess()を呼び出し、ボタンを無効にします
  • doInBackground()でバックグラウンド スレッドを待機します。
  • done()でUI を更新します

この方法では、EDT がフリーズすることはありません...

もう 1 つのオプションは、独自の "mini-swingworker" を実行することです。

loginButton.setEnabled(false);  // assuming this runs in the EDT
new Thread(new Runnable() {
  public void run() {
    myOtherBgThread.join();  // here we wait for the BG thread
    SwingUtilities.invokeLater(new Runnable() {
      // go back into the EDT
      // update the UI here...
    }
  }
}.start();

それが役立つことを願っています、よろしく

于 2013-01-11T10:53:11.880 に答える
1
  1. 最も簡単な解決策の 1 つは、最初の DB 通信が完了する前にログイン ボタンを無効にすることです。ただし、ユーザーを混乱させないように、初期化が行われるという追加の進行状況バーまたはメッセージが必要になります。この場合SwingWorker、終了時にログインボタンを有効にするものを使用できます。

  2. もう 1 つの解決策は、ユーザーが [ログイン] ボタンを押したときに、最初の DB 通信スレッドが参加するのを待つ (別の) バックグラウンド スレッドに処理を再度委任することです。このようにして、EDT (および GUI) はブロックされません。実際に DB に接続するのも時間のかかる操作なので、1 で説明したようにログイン ボタンを無効にしても、とにかくバックグラウンド スレッドで実行する必要があります。

  3. さらに別の解決策は、固定サイズ 1 (例: ) のスレッド プールを作成し、java.util.concurrent.Executors.newSingleThreadExecutor()そこにすべてのバックグラウンド タスクを渡すことです。シングル スレッド エグゼキュータ タスクは、順番に実行されます。最初のタスクは最初の DB 通信で、もう 1 つはログイン タスクです。

于 2013-01-11T10:37:14.770 に答える
0

ExecutorServiceを使用します。executorService.submit(runnable)'runnable'がDB作業を実行する場所を呼び出します。submitメソッドはを返しますFuture。次に、ログイン後future.get()、完了を待つ(または、ランナブルがすでに完了している場合はすぐに戻る)を呼び出すことができます。

于 2013-01-11T11:26:31.097 に答える