如何让JavaSwing正确绘制?

如何让JavaSwing正确绘制?,java,swing,paint,Java,Swing,Paint,我有一个显示Java Swing GUI更新问题的最小应用程序,似乎没有正确绘制组件,错误如下: while (!executor.isTerminated()) { executor.awaitTermination(100, TimeUnit.MILLISECONDS); } while (!executor.isTerminated()) { executor.awaitTermination(100, TimeUnit.MILLISECONDS); } 这是我的节目

我有一个显示Java Swing GUI更新问题的最小应用程序,似乎没有正确绘制组件,错误如下:

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
这是我的节目:

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

public class Java_Test extends JPanel
{
  static JFrame frame=new JFrame("Java Test");
  static int W=800,H=260,Executor_Count=12;
  JPanel Progress_Panel=new JPanel(),Center_Panel;
  JButton Do_Test_Button=new JButton("Do Test");
  Get_Time Timer=new Get_Time();
  ThreadPoolExecutor executor;
  Progress_Bar Progress_bar;
  Timer Display_Timer=new Timer(1);

  public Java_Test()
  {
    setLayout(new BorderLayout());

    JPanel Top_Panel=new JPanel();
    Top_Panel.setPreferredSize(new Dimension(W-6,60));
    add("North",Top_Panel);

    Do_Test_Button.setFont(new Font("Times New Roman",0,16));
    Do_Test_Button.setBackground(new Color(118,198,250));
    Do_Test_Button.setForeground(new Color(0,28,218));
    Do_Test_Button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Do_Test(); } });
    Top_Panel.add(Do_Test_Button);

    JPanel Center_Panel=new JPanel();
    Center_Panel.setPreferredSize(new Dimension(W-2,170));
    add("Center",Center_Panel);        

    Progress_Panel.setPreferredSize(new Dimension(W-2,160));
    Center_Panel.add(Progress_Panel);    

    JLabel Progress_Label=new JLabel("Progress");
    Progress_Label.setFont(new Font("Times New Roman",0,20));
    Progress_Label.setBackground(new Color(253,253,253));
    Progress_Label.setForeground(new Color(8,68,128));
    Progress_Label.setPreferredSize(new Dimension(W-20,53));
    Progress_Label.setHorizontalAlignment(SwingConstants.CENTER);
    Progress_Panel.add(Progress_Label);

    Progress_bar=new Progress_Bar(620,26);
    Progress_Panel.add(Progress_bar);

    JPanel Time_Used_Panel=new JPanel(new FlowLayout(FlowLayout.CENTER,6,26));
    Time_Used_Panel.setPreferredSize(new Dimension(W-20,60));
    Progress_Panel.add(Time_Used_Panel);

    JLabel Time_Used_Label=new JLabel("Time : ");
    Time_Used_Label.setFont(new Font("Times New Roman",0,14));
    Time_Used_Label.setForeground(new Color(0,0,238));
    Time_Used_Label.setHorizontalAlignment(SwingConstants.CENTER);
    Time_Used_Panel.add(Time_Used_Label);

    Display_Timer.setFont(new Font("Times New Roman",0,14));
    Display_Timer.setForeground(new Color(0,0,238));
    Display_Timer.setPreferredSize(new Dimension(50,17));
    Time_Used_Panel.add(Display_Timer);

    Clock clock=new Clock(0);
    clock.setFont(new Font("Monospaced",Font.PLAIN,16));
    clock.setBackground(new Color(0,110,220));
    clock.setForeground(new Color(250,250,250));
    clock.setOpaque(true);
    clock.setPreferredSize(new Dimension(288,30));
    clock.start();

    JPanel Bottom_Panel=new JPanel();
    Bottom_Panel.setPreferredSize(new Dimension(W-2,50));
    Bottom_Panel.add(clock);

    add("South",Bottom_Panel);
    setPreferredSize(new Dimension(W,H));
  }

  void Do_Test()
  {
    String Info="",Result;
    Out("Do_Test");
    try
    {
      Display_Timer.start();
      Timer.Start();
      Output_Time("[ 1 ]");
      Progress_bar.Set_Progress(1);

      int Task_Count=222;
      executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue());
      ArrayList<Future<String>> futures=new ArrayList<>(Task_Count);
      Test_Runner A_Runner;

      try
      {
        for (int i=0;i<Task_Count;i++)
        {
          A_Runner=new Test_Runner();
          futures.add(executor.submit(A_Runner));
        }
        executor.shutdown();
        while (!executor.isTerminated()) { executor.awaitTermination(100,TimeUnit.MILLISECONDS); }

        for (Future<String> future : futures)
        {
          Result=future.get();
          if (Result!=null) Info+=Result;
        }
      }
      catch (Exception e) { e.printStackTrace(); }
    }
    catch (Exception e) { e.printStackTrace(); }
    Output_Time("[ 2 ]");
    Progress_bar.Set_Progress(100);
    Out("Done");
    Display_Timer.stop();
  }

  String Output_Time(String Id)
  {
    Timer.End();
    String Time_Duration=Id+" : Time = "+Timer.Get_Duration_Hour_Minute_Second();
    Out(Time_Duration);
    return Time_Duration;
  }

  private static void Out(String message) { System.out.println(message); }

  static void Create_And_Show_GUI()
  {
    final Java_Test demo=new Java_Test();

    frame.add(demo);
    frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } }); }
}

class Progress_Bar extends JPanel implements Runnable
{
  int W,H,Last_Progress=-99,Progress,counter=0,Unit_Size=20;
  static JProgressBar b=new JProgressBar();
  boolean Started_B=false,Do_Step_B=false;
  Thread Progress_Bar_Runner_Thread;

  public Progress_Bar(int W,int H)
  {
    setPreferredSize(new Dimension(W,H));
    b.setPreferredSize(new Dimension(W-20,H-8));
    b.setStringPainted(true);
    add(b);
    start();
  }

  public void Set_Progress(int Progress)
  {
    if (Progress==1 || (this.Progress<Progress && Progress<=100))
    {
      this.Progress=Progress;
      b.setValue(Progress);
      b.paintImmediately(0,0,b.getWidth(),b.getHeight());
      Out("        Progress = "+Progress+" %");
    }
    if (Progress==1) Started_B=true;
    else if (Progress>=100)
    {
      Started_B=false;
      Do_Step_B=false;
    }
  }

  public void run()
  {
    try
    {
      while (Progress<=100)
      {
        if ((Progress==0 || Progress==50 || Progress==100 || Do_Step_B) && Last_Progress!=Progress)
        {
          b.setValue(Progress);
//          revalidate();
          b.paintImmediately(0,0,b.getWidth(),b.getHeight());
          Last_Progress=Progress;
        }
        Thread.sleep(200);                                                      // Delay the thread
        Do_Step_B=(Started_B && (counter++ % Unit_Size ==0));
        if (Progress<100 && Do_Step_B) Progress++;
      }
    }
    catch (Exception e) { e.printStackTrace(); }
  }

  public void start()
  {
    if (Progress_Bar_Runner_Thread==null)
    {
      Progress_Bar_Runner_Thread=new Thread(this);
      Progress_Bar_Runner_Thread.setPriority(Thread.NORM_PRIORITY);
      Progress_Bar_Runner_Thread.start();
    }
  }

  public void stop() { if (Progress_Bar_Runner_Thread!=null) Progress_Bar_Runner_Thread=null; }

  private static void Out(String message) { System.out.println(message); }
}

class Test_Runner implements Callable<String>
{
  int Start_Index=0,End_Index=6999;
  StringBuilder StrBdr=new StringBuilder();

  public Test_Runner() { }

  public String call() throws InterruptedException
  {
    try { for (int i=Start_Index;i<End_Index;i++) StrBdr.append("Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner + Test_Runner"); }
    catch (Exception e) {}
    return StrBdr.toString();
  }
}

class Timer extends JLabel implements Runnable
{
  public static final long serialVersionUID=26362862L;
  private Thread Timer_Thread;
  String Time_Text="";
  int updateInterval=1000,Format=0;
  Get_Time Timer=new Get_Time();

  public Timer()
  {
    setFont(new Font("Monospaced",Font.PLAIN,16));
    setVerticalAlignment(SwingConstants.CENTER);
    setHorizontalAlignment(SwingConstants.CENTER);
  }

  public Timer(int Format) { this.Format=Format; }

  public void start()
  {
    Timer.Start();
    if (Timer_Thread==null)
    {
      Timer_Thread=new Thread(this);
      Timer_Thread.setPriority(Thread.NORM_PRIORITY);
      Timer_Thread.start();
    }
  }

  public void run()
  {
    Thread myThread=Thread.currentThread();
    while (Timer_Thread==myThread)
    {
      switch (Format)
      {
        case 1 : Time_Text=Timer.Get_Duration_Hour_Minute_Second();break;
      }
      setText(Time_Text);
      paintImmediately(0,0,getWidth(),getHeight());
      revalidate();
      try { Thread.sleep(updateInterval); }
      catch (InterruptedException e) { } 
    }
  }

  public void stop() { if (Timer_Thread != null) Timer_Thread=null; }
}

class Clock extends JLabel implements Runnable
{
  public static final long serialVersionUID=26362862L;
  private Thread clockThread;
  String Time_Text="";
  int updateInterval=1000,Format=0;
  Get_Time Timer=new Get_Time();

  public Clock() { start(); }

  public Clock(int Format)
  {
    this.Format=Format;
    start();
  }

  public void start()
  {
    setVerticalAlignment(SwingConstants.CENTER);
    setHorizontalAlignment(SwingConstants.CENTER);
    if (clockThread==null)
    {
      clockThread=new Thread(this);
//      clockThread.setPriority(Thread.NORM_PRIORITY);
      clockThread.setPriority(Thread.MIN_PRIORITY);
      clockThread.start();
    }
  }

  public void run()
  {
    Thread myThread=Thread.currentThread();

    while (clockThread==myThread)
    {
      switch (Format)
      {
        case 0 : Time_Text=" "+new java.util.Date().toString().substring(0,19)+" ";break;
      }
      setText(Time_Text);
      paintImmediately(0,0,getWidth(),getHeight());
      revalidate();

      try { Thread.sleep(updateInterval); }
      catch (InterruptedException e) { } 
    }
  }

  public void stop() { if (clockThread != null) clockThread=null; }
}

class Get_Time
{
  private long start,end;
  public int Hours,Minutes,Seconds,Total_Seconds;
  String ST_Hours,ST_Minutes,ST_Seconds;

  public Get_Time() { Reset(); }

  public void Start() { start=System.currentTimeMillis(); }

  public void End()
  {
    int half_second;

    end=System.currentTimeMillis();

    Total_Seconds=(int)(end-start)/1000;
    half_second=(int)(end-start)%1000;

    if (half_second>499) Total_Seconds++;

    Hours=Total_Seconds/3600;
    Minutes=(Total_Seconds%3600)/60;
    Seconds=(Total_Seconds%3600)%60;

    ST_Hours=new String((Hours>9)?""+Hours:"0"+Hours);
    ST_Minutes=new String((Minutes>9)?""+Minutes:"0"+Minutes);
    ST_Seconds=new String((Seconds>9)?""+Seconds:"0"+Seconds);
  }

  public String Get_Duration_Hour_Minute_Second()
  {
    End();
    return ST_Hours+":"+ST_Minutes+":"+ST_Seconds;
  }

  public void Reset() { start=0;end=0; }
}
while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
import java.awt.*;
导入java.awt.event.*;
导入java.util.*;
导入javax.swing.*;
导入java.util.concurrent.*;
公共类Java_测试扩展了JPanel
{
静态JFrame=新JFrame(“Java测试”);
静态整数W=800,H=260,执行器计数=12;
JPanel Progress_Panel=新的JPanel(),中心_Panel;
JButton Do_Test_按钮=新JButton(“Do Test”);
获取时间计时器=新获取时间();
线程池执行器;
进度条进度条;
定时器显示\定时器=新定时器(1);
公共Java_测试()
{
setLayout(新的BorderLayout());
JPanel Top_Panel=新的JPanel();
顶部面板。设置首选尺寸(新尺寸(W-6,60));
添加(“北”,顶部面板);
设置字体(新字体(“Times new Roman”,0,16));
Do_Test_按钮。设置背景(新颜色(118198250));
设置前景(新颜色(0,28218));
Do_Test_Button.addActionListener(新建ActionListener(){public void actionPerformed(ActionEvent evt){Do_Test();}});
顶部面板。添加(执行测试按钮);
JPanel中心面板=新JPanel();
中心面板。设置首选尺寸(新尺寸(W-2170));
添加(“中心”,中心面板);
进度面板设置首选尺寸(新尺寸(W-2160));
中心面板。添加(进度面板);
JLabel Progress_Label=新JLabel(“进度”);
Progress_Label.setFont(新字体(“Times new Roman”,0,20));
进度标签。挫折背景(新颜色(253));
Progress_Label.setForeground(新颜色(8,68128));
进度标签设置首选尺寸(新尺寸(W-20,53));
进度标签设置水平对齐(SwingConstants.CENTER);
进度面板。添加(进度标签);
进度条=新进度条(620,26);
进度面板。添加(进度条);
JPanel Time_Used_Panel=newjpanel(newflowlayout(FlowLayout.CENTER,6,26));
使用的时间面板。设置首选尺寸(新尺寸(W-20,60));
进度面板。添加(使用时间面板);
JLabel Time_Used_Label=新JLabel(“Time:”);
Time_Used_Label.setFont(新字体(“Times new Roman”,0,14));
使用时间标签设置前景(新颜色(0,0238));
使用的时间\u标签。设置水平对齐(SwingConstants.CENTER);
使用时间面板。添加(使用时间标签);
显示_Timer.setFont(新字体(“Times new Roman”,0,14));
显示定时器设置前景(新颜色(0,0238));
显示计时器设置首选尺寸(新尺寸(50,17));
使用的时间面板。添加(显示计时器);
时钟=新时钟(0);
clock.setFont(新字体(“等距”,普通字体,16));
时钟背景(新颜色(011020));
时钟设置前景(新颜色(250250));
clock.setOpaque(真);
时钟。设置首选尺寸(新尺寸(288,30));
clock.start();
JPanel Bottom_Panel=新的JPanel();
底部面板。设置首选尺寸(新尺寸(W-2,50));
底部面板。添加(时钟);
添加(“南”,底部面板);
设置首选尺寸(新尺寸(W,H));
}
void Do_Test()
{
字符串Info=“”,结果;
Out(“Do_测试”);
尝试
{
显示_Timer.start();
Timer.Start();
输出时间(“[1]”);
进度条。设置进度(1);
int Task_Count=222;
executor=新线程池executor(executor_Count,executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue());
ArrayList futures=新的ArrayList(任务计数);
测试跑步者A跑步者;
尝试
{

对于(int i=0;iSwing线程和喷漆问题的枚举:

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
以下所有代码都是从Swing事件调度线程(EDT)调用的,到目前为止还不错

void Do_Test() {
    String Info = "", Result;
    Out("Do_Test");
    try {
        Display_Timer.start();
        Timer.Start();
        Output_Time("[ 1 ]");
        Progress_bar.Set_Progress(1);   // OK to call this on the event thread

        int Task_Count = 222;
        executor = new ThreadPoolExecutor(Executor_Count, Executor_Count * 2, 1,
                TimeUnit.SECONDS, new LinkedBlockingQueue());
        ArrayList<Future<String>> futures = new ArrayList<>(Task_Count);
        Test_Runner A_Runner;

        try {
            for (int i = 0; i < Task_Count; i++) {
                A_Runner = new Test_Runner();
                futures.add(executor.submit(A_Runner));
            }
            executor.shutdown();
while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
这里也是一样,因为对
Future#get()
的调用是一个阻塞调用:

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
for (Future<String> future : futures) {
    Result = future.get();
    if (Result != null)
        Info += Result;
}
类似的奇怪情况也出现在这个类中,即Timer类中,在该类中,您可以设置EDT的JLabel文本off

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
类计时器扩展JLabel实现可运行{ //

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
public void run() {
    Thread myThread = Thread.currentThread();
    while (Timer_Thread == myThread) {
        switch (Format) {
        case 1:
            Time_Text = Timer.Get_Duration_Hour_Minute_Second();
            break;
        }

        // again this is dangerous code **********
        setText(Time_Text);
        paintImmediately(0, 0, getWidth(), getHeight());
        // ....
同样适用于时钟类

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}

解决方案:

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
要在Swing GUI中重复调用代码,请使用
javax.Swing.Timer
或“Swing Timer”。有关此用法的示例,请参阅我的MCVE实现,但在上面的线程代码中使用Swing定时器

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
另一个代码,即调用长时间运行的任务的代码,应该在SwingWorker中完成。此工作线程通常通过两种方式之一(或两者)与Swing GUI通信——要么使用发布/进程方法对,要么(如下面的示例所示)使用附加到工作线程的PropertyChangeListener。工作线程有几个“绑定”属性,用于通知侦听器更改的字段,包括可保存0到100之间的值的
progress
属性,以及SwingWorker.StateValue或“state”属性:

while (!executor.isTerminated()) {
    executor.awaitTermination(100, TimeUnit.MILLISECONDS);
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.*;

@SuppressWarnings("serial")
public class JavaTest2 extends JPanel {
    private static final int W = 800;
    private static final int H = 260;
    private Action doTestAction = new DoTestAction("Do Test");
    private JProgressBar progressBar = new JProgressBar(0, 100);
    private MyClockPanel clockPanel = new MyClockPanel();
    private MyTimerPanel timerPanel = new MyTimerPanel();

    public JavaTest2() {
        JPanel topPanel = new JPanel();
        topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 35, 5));
        topPanel.add(new JButton(doTestAction));

        progressBar.setStringPainted(true);
        JPanel progressPanel = new JPanel(new GridBagLayout());
        progressPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        progressPanel.add(progressBar, gbc);

        JLabel progressLabel = new JLabel("Progress", SwingConstants.CENTER);
        progressLabel.setFont(new Font("Times New Roman", 0, 20));
        progressLabel.setForeground(new Color(8, 68, 128));

        JPanel centralPanel = new JPanel(new BorderLayout(5, 5));
        centralPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        centralPanel.add(progressLabel, BorderLayout.PAGE_START);
        centralPanel.add(progressPanel);

        JPanel clockWrapper = new JPanel();
        clockWrapper.add(clockPanel);
        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS));
        bottomPanel.add(timerPanel, BorderLayout.PAGE_START);
        bottomPanel.add(clockWrapper, BorderLayout.PAGE_END);

        setLayout(new BorderLayout());
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        add(topPanel, BorderLayout.PAGE_START);
        add(bottomPanel, BorderLayout.PAGE_END);
        add(centralPanel);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        if (isPreferredSizeSet()) {
            return superSize;
        } else {
            int w = Math.max(superSize.width, W);
            int h = Math.max(superSize.height, H);
            return new Dimension(w, h);
        }
    }

    private class DoTestAction extends AbstractAction {
        private MyWorker myWorker = null;

        public DoTestAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (myWorker != null && myWorker.getState() == SwingWorker.StateValue.STARTED) {
                return; // still running
            }
            timerPanel.start();
            progressBar.setValue(0);
            myWorker = new MyWorker();
            myWorker.addPropertyChangeListener(new WorkerListener());
            myWorker.execute();
            setEnabled(false);
        }
    }

    class WorkerListener implements PropertyChangeListener {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            // if the worker is changing its progress bound property:
            if (evt.getPropertyName().equals("progress")) {
                int progress = (int) evt.getNewValue();
                // just for safety's sake, limit progress to 100 and no more
                progress = Math.min(progress, 100);
                progressBar.setValue(progress);
            } else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                // else if worker is done
                try {
                    // get the result to at least trap errors
                    String result = ((MyWorker) evt.getSource()).get();
                    // can display result in the GUI
                    timerPanel.stop();
                } catch (Exception e) {
                    // worker's exception is available to the GUI if desired here
                    e.printStackTrace();
                }
                progressBar.setValue(100);
                doTestAction.setEnabled(true);
            }
        }
    }

    private static class MyWorker extends SwingWorker<String, Void> {
        private static final int EXECUTOR_COUNT = 12;
        private static final int TASK_COUNT = 222;

        @Override
        protected String doInBackground() throws Exception {
            ExecutorService executor = new ThreadPoolExecutor(EXECUTOR_COUNT, EXECUTOR_COUNT * 2, 1,
                    TimeUnit.SECONDS, new LinkedBlockingQueue<>());
            List<Future<String>> futures = new ArrayList<>();
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < TASK_COUNT; i++) {
                Callable<String> aRunner = new ARunner();
                futures.add(executor.submit(aRunner));
            }
            executor.shutdown();
            int index = 0;
            for (Future<String> future : futures) {
                String result = future.get();
                sb.append(result);
                sb.append(" ");
                index++;
                int progress = (100 * index) / TASK_COUNT;
                progress = Math.min(progress, 100);
                setProgress(progress);
            }

            return sb.toString();
        }
    }

    private static class ARunner implements Callable<String> {
        private static final long SLEEP_TIME = 800;

        @Override
        public String call() throws Exception {
            TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
            return "Foo";
        }
    }

    private static void createAndShowGui() {
        JavaTest2 mainPanel = new JavaTest2();

        JFrame frame = new JFrame("Java Test 2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

您的线程可能就是问题所在。从AWT事件调度线程以外的任何线程调用Swing方法和构造函数都是非法的。违反此规则将导致奇怪的行为。要在正确的线程中定期执行任务,请使用(而不是java.util.Timer)。有关详细信息,请参阅。您正在使用Swing绘画库,通过从后台线程调用
paintimmediate
。解决方案不是这样做,而是使用该库,正如教程和本网站上的许多Swing动画Q/A中所述。另外,您还需要学习和使用.Variable名称都应该以小写字母开头,而类名应该以大写字母开头。学习这一点并遵循这一点将使我们能够更好地理解您的代码,并使您能够更好地理解其他人的代码。下面是一个简单的示例,演示如何使用Swing计时器更新日期/时间:。基本上是几行代码太棒了!我做Java Swing已经很长时间了,我认为我很擅长,但显然不是,总有很多东西要学,就像俗话说的,知道的越多,不知道的越多