在Swing中调用后台线程

3

首先的代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class cos {
   public static int a;

   private static JLabel labeler;

   // public static Runnable r1;

   private JFrame frame;

   /**
    * Launch the application.
    */
   public static void main(String[] args) {
      a = 0;
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            try {
               cos window = new cos();
               window.frame.setVisible(true);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });

   }

   /**
    * Create the application.
    */
   public cos() {
      initialize();
   }

   /**
    * Initialize the contents of the frame.
    */
   public void initialize() {
      frame = new JFrame();
      frame.setBounds(100, 100, 205, 194);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      JLabel lblTime = new JLabel("Time:");
      frame.getContentPane().add(lblTime, BorderLayout.WEST);

      final JLabel labeler = new JLabel("");
      frame.getContentPane().add(labeler, BorderLayout.CENTER);

      JButton btnNewButton = new JButton("New button");
      btnNewButton.addActionListener(new ActionListener() {
         Runnable r1 = new Runnable() {
            public void run() {
               while (a <= 10) {
                  a = a + 1;
                  labeler.setText(Integer.toString(a));
                  try {
                     Thread.sleep(1000);
                  } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                  }
               }

            }
         };

         public void actionPerformed(ActionEvent arg0) {
            Thread threder = new Thread(r1);
            threder.start();
            // liczniczek bla = new liczniczek();

         }
      });
      frame.getContentPane().add(btnNewButton, BorderLayout.SOUTH);

   }

   public void licznik() {
      while (a < 60) {
         a = a + 1;
         labeler.setText(Integer.toString(a));
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
      }
   }
}

现在,我有一个问题。我想使用以下代码:

             Runnable r1 = new Runnable(){
                public void run(){
                           licznik();

                }
            };

但是这并不起作用。我该怎么做才能将这段代码分开呢?抱歉我的英语不好。

Sierran。

5个回答

3

在 EDT(事件分派线程)期间永远不要使用 Thread#sleep(int),如果只有这个线程存在,则可以正确工作(阻塞 EDT)。

Runnable r1 = new Runnable(){
   public void run(){
       licznik();
   }
};

如果你直接调用 licznik(); 是错误的,你需要这样包装它

Runnable r1 = new Runnable(){
   public void run(){
       labeler.setText(Integer.toString(a));    
   }
};

但是如果没有 Thread#sleep(int),你有三个选择:

1) 将 Thread 更改为 javax.swing.Timer

2) 将 Thread 更改为 Runnable#Thread,在那里你可以用 Thread#sleep(int) 进行延迟,但输出到 GUI 必须是

Runnable r1 = new Runnable(){
   public void run(){
       labeler.setText(Integer.toString(a));    
   }
};

3) 使用SwingWorker,其中输出在EDT中,您也可以使用Thread#sleep(int)

示例在EDT期间使用Thread#sleep(int)

将它们全部放在一起

编辑

  • 不要在编程语言中使用保留字作为类、方法、变量、任何名称(意思是cos

  • 通过实现我在这里发布的所有三个选项,您的代码可以正常工作。


谢谢您的想法 :) 但是:代码:http://wklej.to/j8OOH 同样的错误。线程“AWT-EventQueue-0”中的异常java.lang.NullPointerException。 - Sierran
抱歉,我不会调试任何东西。很抱歉,我在这里只是为了自己的乐趣和学习英语和Swing。 - mKorbel
是的 ;) 现在我有线程,但当labeler.setText(Integer.toString(a));不同于初始化函数时,我会遇到错误。但感谢您的一切 :) - Sierran
现在所有与计时器有关的工作都很好。只有labeler.setText(Integer.toString(a));会出错。现在代码是这样的:http://wklej.to/eI2xs。 - Sierran
@Sierran 哈哈哈 :-) 简单的错误,你从错误的一侧拉了出来 :-) labeler.setText(String.valueOf(a)); - mKorbel
显示剩余2条评论

2
你说“它不起作用”是什么意思?对我而言它是有效的。你是怎样尝试使用这段代码的?在运行时你遇到了哪些错误或问题?我个人会使用SwingWorker,并通过SwingWorker的publish/process方法对JLabel的文本进行设置。要了解更多信息,请参阅此教程:Swing中的并发 编辑
实际上,实现你想要的更简单的方法是根本不直接使用线程或可运行对象,而是使用Swing定时器,因为它们专门针对这种情况构建。要了解更多信息,请查看Swing定时器教程

抱歉,当我输入我的答案时,我没有看到您的更新,之前的+1。 - mKorbel
@mKorbel:没问题。我们再次在大多数观点上达成了一致,所以我必须给你点赞! :) - Hovercraft Full Of Eels

1

我了解您想要在单独的线程中运行函数licznik()。您创建了一个Runnable,但是还需要做更多的事情才能使其run()方法执行。有几种方法可以实现这一点:

Runnable r1 = new Runnable(){
    public void run(){
        licznik();
    }
};
new Thread(r1).start();

或者你可以直接继承Thread类:

Thread r1 = new Thread(){
    public void run(){
        licznik();
    }
};
r1.start();

仍然出现错误:线程“Thread-3”中的异常java.lang.NullPointerException 在cos.java的第86行cos.licznik - Sierran
需要一个continuation - trashgod
@mKorbel - 你说得对。我没有查看licznik()函数的作用。 - Ted Hopp

0

Runnable接口没有licznik()方法。您可以创建一个实现了licznik()方法的类来实现Runnable接口。

或者,如果您不需要重复使用此方法并且只使用一次,则最快的方法是将其实现移动到新的Runnable()块内部。

         Runnable r1 = new Runnable(){
            public void run(){
                       this.licznik();

            }
            public void licznik(){
                while (a < 60){
                a = a + 1 ;
                labeler.setText(Integer.toString(a));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            }
        }
        }
        };

好的。但是如果我想在initialize函数之外使用licznik()函数呢? - Sierran

0
在GitHub上查看https://github.com/greggwon/Ham。查看https://github.com/greggwon/Ham/blob/master/SwingUtil/src/org/wonderly/swing/ComponentUpdateThread.java中的源代码,了解我如何将整个细节打包到单个类中,并使用匿名内部类来完成工作。现在可以将其更改为Lambda,但我已经数年没有使用Java了,因此还没有进行更改。
    new ComponentUpdateThread( new Action[] { add, del, edit } ) {
        public void setup() {
            super.setup();
            list.setEnabled(false);
            list.clearSelection();
        }
        public Object construct() {
            try {
                Vector v = remote.getData();
                Collections.sort( v );
                return v;
            } catch( Exception ex ) {
                reportException(ex);
            }
            return null;
        }
        public void finished() {
            try {
                Vector v = (Vector)getValue();
                if( v != null ) list.setListData(v);
            } finally {
                super.finished();
                list.setEnabled(true);
                edit.setEnabled(false);
                del.setEnaled(false);
            }
        }
    }.start();

通过这种工作方式,您可以使用周围块或其他类可见数据的最终值来控制在后台线程执行之前、期间和之后发生的各个方面。

这些年来,我已经以各种方式改变了这段代码,并且存在其他变体。

ComponentUpdateThread构造函数的参数是要在后台线程运行时“禁用”的控件/操作。其他启用/禁用活动可以更直接地嵌入到setup()和finished()中的活动中(这些活动在AWT事件线程中运行),然后在后台线程中运行“construct”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接