javax.swing.Timer减速

javax.swing.Timer slowdown in Java7u40

同时调用 javax.swing.Timer#start(),

7u25 没问题。

关于Java7u40中java:javax.swing.Timer减速关于Java7u40中java:javax.swing.Timer减速

但是7u40是个大问题。

关于Java7u40中java:javax.swing.Timer减速关于Java7u40中java:javax.swing.Timer减速

调用 ActionListener#actionPerformed 过于滞后。 (基本上同时调用 u25)

u25 和 u40 之间的移动完全不同。 (我使用的是 Windows 8)

我报告了错误,但仍未添加错误跟踪系统。甲骨文粉碎摇摆应用程序?

import java.awt.*;

import java.awt.event.*;



import javax.swing.*;



public class TimerProblem extends JComponent {



    int red = 0;



    TimerProblem(final long startMs) {

        setPreferredSize(new Dimension(10, 10));



        Timer t = new Timer(16, new ActionListener() {



            @Override

            public void actionPerformed(ActionEvent e) {

                red = (int)(System.currentTimeMillis() - startMs) % 255;

                repaint();

            }



        });

        t.setInitialDelay(1000);

        t.start();

    }



    @Override

    protected void paintComponent(Graphics g) {

        g.setColor(new Color(red, 255 - red, 0));

        g.fillRect(0, 0, getWidth(), getHeight());

    }



    public static void main(String[] args) {

        JFrame f = new JFrame();

        Container c = f.getContentPane();



        c.setLayout(new GridLayout(10, 10));

        long startMs = System.currentTimeMillis();

        for (int i = 0; i < 100; i++) {

            c.add(new TimerProblem(startMs));

        }

        f.pack();

        f.setVisible(true);

    }



}

import java.awt.Color;

import java.awt.Dimension;

import java.awt.EventQueue;

import java.awt.Graphics;

import java.awt.GridLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Random;

import javax.swing.*;



/** @see https://stackoverflow.com/a/18936444/230513 */

public class BlinkenLights {



  private static final int S = 24;

  private static final int N = 10;

  private static final Random r = new Random();

  private static final List<MyComponent> list = new ArrayList<MyComponent>();



  private static final class MyComponent extends JComponent {



    public MyComponent() {

      this.setOpaque(true);

    }



    @Override

    public Dimension getPreferredSize() {

      return new Dimension(S, S);

    }



    @Override

    protected void paintComponent(Graphics g) {

      g.setColor(Color.getHSBColor(r.nextFloat() / 6, 1, 1));

      g.fillRect(0, 0, getWidth(), getHeight());

    }

  }



  private static JPanel createPanel() {

    final JPanel p = new JPanel();

    p.setLayout(new GridLayout(N, N));

    for (int i = 0; i < N * N; i++) {

      MyComponent c = new MyComponent();

      p.add(c);

      list.add(c);

    }

    Timer t = new Timer(1000 / N, new ActionListener() {

      @Override

      public void actionPerformed(ActionEvent e) {

        Collections.shuffle(list, r);

        for (int i = 0; i < N; i++) {

          list.get(i).repaint();

        }

      }

    });

    t.start();

    return p;

  }



  public static void main(String[] args) {

    EventQueue.invokeLater(new Runnable() {

      @Override

      public void run() {

        JFrame f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.add(createPanel());

        f.pack();

        f.setLocationRelativeTo(null);

        f.setVisible(true);

      }

    });

  }

}

import java.awt.event.*;

import java.util.*;



import javax.swing.*;

import javax.swing.Timer;



/**

* EffectTimer

*/

public class EffectTimer {



  /**

  * All of effect timers in instance of this class.

  */

  static class GlobalTimer implements ActionListener {



    List<EffectTimer> registeredEffects = new ArrayList<>();



    Timer timer = new Timer(16, this);



    public void start(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          internalStart(t);

        }



      });

    }



    void internalStart(EffectTimer t) {

      int initialDelay = Math.max(0, (int) (t.getEffectStartTime() - System.currentTimeMillis()));

      if(timer.getInitialDelay() >= initialDelay) {

        timer.setInitialDelay(initialDelay);

      }

      if(!registeredEffects.contains(t)) {

        registeredEffects.add(t);

        if(registeredEffects.size() == 1) {

          timer.start();

        }

      }

    }



    void stop(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.remove(t);

          checkStop();

        }



      });

    }



    @Override

    public void actionPerformed(ActionEvent e) {

      long now = e.getWhen();



      Iterator<EffectTimer> iter = registeredEffects.iterator();

      while(iter.hasNext()) {

        EffectTimer t = iter.next();

        long elapsedMs = now - t.getEffectStartTime();



        if(elapsedMs > 0) {

          float p = elapsedMs / (float)t.getEffectLengthMs();

          if(p >= 1.0f) {

            iter.remove();

            t.stop();

          } else {

            if(t.isReversed()) {

              p = 1.0f - p;

            }

            t.progressChanged(p);

          }

        }

      }



      checkStop();

    }



    void checkStop() {

      if(registeredEffects.isEmpty()) {

        timer.stop();

      }

    }



    public int getRunningTimerCount() {

      return registeredEffects.size();

    }



    public void stopAll() {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.clear();

          checkStop();

        }



      });

    }



  }



  static final GlobalTimer GTIMER = new GlobalTimer();



  int effectLengthMs = -1;



  long effectStartMs = -1;



  float progress = 0.0f;



  boolean reversed = true;



  public long getEffectStartTime() {

    return effectStartMs;

  }



  public int getEffectLengthMs() {

    return effectLengthMs;

  }



  public void start(int lengthMs) {

    start(lengthMs, System.currentTimeMillis());

  }



  public void start(int lengthMs, long startMs) {

    effectLengthMs = lengthMs;

    effectStartMs = startMs;



    reversed = false;

    progress = 0.0f;

    GTIMER.start(this);

  }



  public boolean isReversed() {

    return reversed;

  }



  public void reverse(final int lengthMs) {

    SwingUtilities.invokeLater(new Runnable() {



      @Override

      public void run() {

        internalReverse(lengthMs);

      }



    });

  }



  void internalReverse(int lengthMs) {

    reversed = !reversed;



    effectLengthMs = lengthMs;

    int adjust = reversed ? (int)(lengthMs * (1.0f - progress)) : (int)(lengthMs * progress);

    effectStartMs = System.currentTimeMillis() - adjust;



    GTIMER.start(this);

  }



  final public void progressChanged(float p) {

    progress = p;

    run(p);

  }



  /**

  * 0.0f to 1.0f effect progress.

  * <wyn>Float.compare(progress, 1.0f) >= 0</wyn> to end progress.

  */

  protected void run(float p) {}



  public void stop() {

    progress = reversed ? 0.0f : 1.0f;

    GTIMER.stop(this);

  }



  public boolean isRunning() {

    return 0.0f < progress && progress < 1.0f;

  }



  public float getProgress() {

    return progress;

  }



  public static int getRunningTimerCount() {

    return GTIMER.getRunningTimerCount();

  }



  public static void stopAll() {

    GTIMER.stopAll();

  }



}

您的示例中出现了几个问题:

  • Swing GUI 对象只能在事件调度线程上构建和操作。

  • 所有 Swing Timer 实例共享一个公共线程,该线程正在饱和。

根据目标,一些替代方案是可能的:

  • 使用单个 Timer 实例,并选择一些分数以按比例更高的速率进行更新。下面的示例随机选择 N 个组件并每 100 毫秒更新一次。

  • 使用 TexturePaint,如下所示。

关于Java7u40中java:javax.swing.Timer减速

import java.awt.*;

import java.awt.event.*;



import javax.swing.*;



public class TimerProblem extends JComponent {



    int red = 0;



    TimerProblem(final long startMs) {

        setPreferredSize(new Dimension(10, 10));



        Timer t = new Timer(16, new ActionListener() {



            @Override

            public void actionPerformed(ActionEvent e) {

                red = (int)(System.currentTimeMillis() - startMs) % 255;

                repaint();

            }



        });

        t.setInitialDelay(1000);

        t.start();

    }



    @Override

    protected void paintComponent(Graphics g) {

        g.setColor(new Color(red, 255 - red, 0));

        g.fillRect(0, 0, getWidth(), getHeight());

    }



    public static void main(String[] args) {

        JFrame f = new JFrame();

        Container c = f.getContentPane();



        c.setLayout(new GridLayout(10, 10));

        long startMs = System.currentTimeMillis();

        for (int i = 0; i < 100; i++) {

            c.add(new TimerProblem(startMs));

        }

        f.pack();

        f.setVisible(true);

    }



}

import java.awt.Color;

import java.awt.Dimension;

import java.awt.EventQueue;

import java.awt.Graphics;

import java.awt.GridLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Random;

import javax.swing.*;



/** @see https://stackoverflow.com/a/18936444/230513 */

public class BlinkenLights {



  private static final int S = 24;

  private static final int N = 10;

  private static final Random r = new Random();

  private static final List<MyComponent> list = new ArrayList<MyComponent>();



  private static final class MyComponent extends JComponent {



    public MyComponent() {

      this.setOpaque(true);

    }



    @Override

    public Dimension getPreferredSize() {

      return new Dimension(S, S);

    }



    @Override

    protected void paintComponent(Graphics g) {

      g.setColor(Color.getHSBColor(r.nextFloat() / 6, 1, 1));

      g.fillRect(0, 0, getWidth(), getHeight());

    }

  }



  private static JPanel createPanel() {

    final JPanel p = new JPanel();

    p.setLayout(new GridLayout(N, N));

    for (int i = 0; i < N * N; i++) {

      MyComponent c = new MyComponent();

      p.add(c);

      list.add(c);

    }

    Timer t = new Timer(1000 / N, new ActionListener() {

      @Override

      public void actionPerformed(ActionEvent e) {

        Collections.shuffle(list, r);

        for (int i = 0; i < N; i++) {

          list.get(i).repaint();

        }

      }

    });

    t.start();

    return p;

  }



  public static void main(String[] args) {

    EventQueue.invokeLater(new Runnable() {

      @Override

      public void run() {

        JFrame f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.add(createPanel());

        f.pack();

        f.setLocationRelativeTo(null);

        f.setVisible(true);

      }

    });

  }

}

import java.awt.event.*;

import java.util.*;



import javax.swing.*;

import javax.swing.Timer;



/**

* EffectTimer

*/

public class EffectTimer {



  /**

  * All of effect timers in instance of this class.

  */

  static class GlobalTimer implements ActionListener {



    List<EffectTimer> registeredEffects = new ArrayList<>();



    Timer timer = new Timer(16, this);



    public void start(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          internalStart(t);

        }



      });

    }



    void internalStart(EffectTimer t) {

      int initialDelay = Math.max(0, (int) (t.getEffectStartTime() - System.currentTimeMillis()));

      if(timer.getInitialDelay() >= initialDelay) {

        timer.setInitialDelay(initialDelay);

      }

      if(!registeredEffects.contains(t)) {

        registeredEffects.add(t);

        if(registeredEffects.size() == 1) {

          timer.start();

        }

      }

    }



    void stop(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.remove(t);

          checkStop();

        }



      });

    }



    @Override

    public void actionPerformed(ActionEvent e) {

      long now = e.getWhen();



      Iterator<EffectTimer> iter = registeredEffects.iterator();

      while(iter.hasNext()) {

        EffectTimer t = iter.next();

        long elapsedMs = now - t.getEffectStartTime();



        if(elapsedMs > 0) {

          float p = elapsedMs / (float)t.getEffectLengthMs();

          if(p >= 1.0f) {

            iter.remove();

            t.stop();

          } else {

            if(t.isReversed()) {

              p = 1.0f - p;

            }

            t.progressChanged(p);

          }

        }

      }



      checkStop();

    }



    void checkStop() {

      if(registeredEffects.isEmpty()) {

        timer.stop();

      }

    }



    public int getRunningTimerCount() {

      return registeredEffects.size();

    }



    public void stopAll() {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.clear();

          checkStop();

        }



      });

    }



  }



  static final GlobalTimer GTIMER = new GlobalTimer();



  int effectLengthMs = -1;



  long effectStartMs = -1;



  float progress = 0.0f;



  boolean reversed = true;



  public long getEffectStartTime() {

    return effectStartMs;

  }



  public int getEffectLengthMs() {

    return effectLengthMs;

  }



  public void start(int lengthMs) {

    start(lengthMs, System.currentTimeMillis());

  }



  public void start(int lengthMs, long startMs) {

    effectLengthMs = lengthMs;

    effectStartMs = startMs;



    reversed = false;

    progress = 0.0f;

    GTIMER.start(this);

  }



  public boolean isReversed() {

    return reversed;

  }



  public void reverse(final int lengthMs) {

    SwingUtilities.invokeLater(new Runnable() {



      @Override

      public void run() {

        internalReverse(lengthMs);

      }



    });

  }



  void internalReverse(int lengthMs) {

    reversed = !reversed;



    effectLengthMs = lengthMs;

    int adjust = reversed ? (int)(lengthMs * (1.0f - progress)) : (int)(lengthMs * progress);

    effectStartMs = System.currentTimeMillis() - adjust;



    GTIMER.start(this);

  }



  final public void progressChanged(float p) {

    progress = p;

    run(p);

  }



  /**

  * 0.0f to 1.0f effect progress.

  * <wyn>Float.compare(progress, 1.0f) >= 0</wyn> to end progress.

  */

  protected void run(float p) {}



  public void stop() {

    progress = reversed ? 0.0f : 1.0f;

    GTIMER.stop(this);

  }



  public boolean isRunning() {

    return 0.0f < progress && progress < 1.0f;

  }



  public float getProgress() {

    return progress;

  }



  public static int getRunningTimerCount() {

    return GTIMER.getRunningTimerCount();

  }



  public static void stopAll() {

    GTIMER.stopAll();

  }



}

终于写了DIY重绘管理类.. :(

import java.awt.*;

import java.awt.event.*;



import javax.swing.*;



public class TimerProblem extends JComponent {



    int red = 0;



    TimerProblem(final long startMs) {

        setPreferredSize(new Dimension(10, 10));



        Timer t = new Timer(16, new ActionListener() {



            @Override

            public void actionPerformed(ActionEvent e) {

                red = (int)(System.currentTimeMillis() - startMs) % 255;

                repaint();

            }



        });

        t.setInitialDelay(1000);

        t.start();

    }



    @Override

    protected void paintComponent(Graphics g) {

        g.setColor(new Color(red, 255 - red, 0));

        g.fillRect(0, 0, getWidth(), getHeight());

    }



    public static void main(String[] args) {

        JFrame f = new JFrame();

        Container c = f.getContentPane();



        c.setLayout(new GridLayout(10, 10));

        long startMs = System.currentTimeMillis();

        for (int i = 0; i < 100; i++) {

            c.add(new TimerProblem(startMs));

        }

        f.pack();

        f.setVisible(true);

    }



}

import java.awt.Color;

import java.awt.Dimension;

import java.awt.EventQueue;

import java.awt.Graphics;

import java.awt.GridLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Random;

import javax.swing.*;



/** @see https://stackoverflow.com/a/18936444/230513 */

public class BlinkenLights {



  private static final int S = 24;

  private static final int N = 10;

  private static final Random r = new Random();

  private static final List<MyComponent> list = new ArrayList<MyComponent>();



  private static final class MyComponent extends JComponent {



    public MyComponent() {

      this.setOpaque(true);

    }



    @Override

    public Dimension getPreferredSize() {

      return new Dimension(S, S);

    }



    @Override

    protected void paintComponent(Graphics g) {

      g.setColor(Color.getHSBColor(r.nextFloat() / 6, 1, 1));

      g.fillRect(0, 0, getWidth(), getHeight());

    }

  }



  private static JPanel createPanel() {

    final JPanel p = new JPanel();

    p.setLayout(new GridLayout(N, N));

    for (int i = 0; i < N * N; i++) {

      MyComponent c = new MyComponent();

      p.add(c);

      list.add(c);

    }

    Timer t = new Timer(1000 / N, new ActionListener() {

      @Override

      public void actionPerformed(ActionEvent e) {

        Collections.shuffle(list, r);

        for (int i = 0; i < N; i++) {

          list.get(i).repaint();

        }

      }

    });

    t.start();

    return p;

  }



  public static void main(String[] args) {

    EventQueue.invokeLater(new Runnable() {

      @Override

      public void run() {

        JFrame f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.add(createPanel());

        f.pack();

        f.setLocationRelativeTo(null);

        f.setVisible(true);

      }

    });

  }

}

import java.awt.event.*;

import java.util.*;



import javax.swing.*;

import javax.swing.Timer;



/**

* EffectTimer

*/

public class EffectTimer {



  /**

  * All of effect timers in instance of this class.

  */

  static class GlobalTimer implements ActionListener {



    List<EffectTimer> registeredEffects = new ArrayList<>();



    Timer timer = new Timer(16, this);



    public void start(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          internalStart(t);

        }



      });

    }



    void internalStart(EffectTimer t) {

      int initialDelay = Math.max(0, (int) (t.getEffectStartTime() - System.currentTimeMillis()));

      if(timer.getInitialDelay() >= initialDelay) {

        timer.setInitialDelay(initialDelay);

      }

      if(!registeredEffects.contains(t)) {

        registeredEffects.add(t);

        if(registeredEffects.size() == 1) {

          timer.start();

        }

      }

    }



    void stop(final EffectTimer t) {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.remove(t);

          checkStop();

        }



      });

    }



    @Override

    public void actionPerformed(ActionEvent e) {

      long now = e.getWhen();



      Iterator<EffectTimer> iter = registeredEffects.iterator();

      while(iter.hasNext()) {

        EffectTimer t = iter.next();

        long elapsedMs = now - t.getEffectStartTime();



        if(elapsedMs > 0) {

          float p = elapsedMs / (float)t.getEffectLengthMs();

          if(p >= 1.0f) {

            iter.remove();

            t.stop();

          } else {

            if(t.isReversed()) {

              p = 1.0f - p;

            }

            t.progressChanged(p);

          }

        }

      }



      checkStop();

    }



    void checkStop() {

      if(registeredEffects.isEmpty()) {

        timer.stop();

      }

    }



    public int getRunningTimerCount() {

      return registeredEffects.size();

    }



    public void stopAll() {

      SwingUtilities.invokeLater(new Runnable() {



        @Override

        public void run() {

          registeredEffects.clear();

          checkStop();

        }



      });

    }



  }



  static final GlobalTimer GTIMER = new GlobalTimer();



  int effectLengthMs = -1;



  long effectStartMs = -1;



  float progress = 0.0f;



  boolean reversed = true;



  public long getEffectStartTime() {

    return effectStartMs;

  }



  public int getEffectLengthMs() {

    return effectLengthMs;

  }



  public void start(int lengthMs) {

    start(lengthMs, System.currentTimeMillis());

  }



  public void start(int lengthMs, long startMs) {

    effectLengthMs = lengthMs;

    effectStartMs = startMs;



    reversed = false;

    progress = 0.0f;

    GTIMER.start(this);

  }



  public boolean isReversed() {

    return reversed;

  }



  public void reverse(final int lengthMs) {

    SwingUtilities.invokeLater(new Runnable() {



      @Override

      public void run() {

        internalReverse(lengthMs);

      }



    });

  }



  void internalReverse(int lengthMs) {

    reversed = !reversed;



    effectLengthMs = lengthMs;

    int adjust = reversed ? (int)(lengthMs * (1.0f - progress)) : (int)(lengthMs * progress);

    effectStartMs = System.currentTimeMillis() - adjust;



    GTIMER.start(this);

  }



  final public void progressChanged(float p) {

    progress = p;

    run(p);

  }



  /**

  * 0.0f to 1.0f effect progress.

  * <wyn>Float.compare(progress, 1.0f) >= 0</wyn> to end progress.

  */

  protected void run(float p) {}



  public void stop() {

    progress = reversed ? 0.0f : 1.0f;

    GTIMER.stop(this);

  }



  public boolean isRunning() {

    return 0.0f < progress && progress < 1.0f;

  }



  public float getProgress() {

    return progress;

  }



  public static int getRunningTimerCount() {

    return GTIMER.getRunningTimerCount();

  }



  public static void stopAll() {

    GTIMER.stopAll();

  }



}

相关推荐

  • Spring部署设置openshift

    Springdeploymentsettingsopenshift我有一个问题让我抓狂了三天。我根据OpenShift帐户上的教程部署了spring-eap6-quickstart代码。我已配置调试选项,并且已将Eclipse工作区与OpehShift服务器同步-服务器上的一切工作正常,但在Eclipse中出现无法消除的错误。我有这个错误:cvc-complex-type.2.4.a:Invali…
    2025-04-161
  • 检查Java中正则表达式中模式的第n次出现

    CheckfornthoccurrenceofpatterninregularexpressioninJava本问题已经有最佳答案,请猛点这里访问。我想使用Java正则表达式检查输入字符串中特定模式的第n次出现。你能建议怎么做吗?这应该可以工作:MatchResultfindNthOccurance(intn,Patternp,CharSequencesrc){Matcherm=p.matcher…
    2025-04-161
  • 如何让 JTable 停留在已编辑的单元格上

    HowtohaveJTablestayingontheeditedcell如果有人编辑JTable的单元格内容并按Enter,则内容会被修改并且表格选择会移动到下一行。是否可以禁止JTable在单元格编辑后转到下一行?原因是我的程序使用ListSelectionListener在单元格选择上同步了其他一些小部件,并且我不想在编辑当前单元格后选择下一行。Enter的默认绑定是名为selectNext…
    2025-04-161
  • Weblogic 12c 部署

    Weblogic12cdeploy我正在尝试将我的应用程序从Tomcat迁移到Weblogic12.2.1.3.0。我能够毫无错误地部署应用程序,但我遇到了与持久性提供程序相关的运行时错误。这是堆栈跟踪:javax.validation.ValidationException:CalltoTraversableResolver.isReachable()threwanexceptionatorg.…
    2025-04-161
  • Resteasy Content-Type 默认值

    ResteasyContent-Typedefaults我正在使用Resteasy编写一个可以返回JSON和XML的应用程序,但可以选择默认为XML。这是我的方法:@GET@Path("/content")@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})publicStringcontentListRequestXm…
    2025-04-161
  • 代码不会停止运行,在 Java 中

    thecodedoesn'tstoprunning,inJava我正在用Java解决项目Euler中的问题10,即"Thesumoftheprimesbelow10is2+3+5+7=17.Findthesumofalltheprimesbelowtwomillion."我的代码是packageprojecteuler_1;importjava.math.BigInteger;importjava…
    2025-04-161
  • Out of memory java heap space

    Outofmemoryjavaheapspace我正在尝试将大量文件从服务器发送到多个客户端。当我尝试发送大小为700mb的文件时,它显示了"OutOfMemoryjavaheapspace"错误。我正在使用Netbeans7.1.2版本。我还在属性中尝试了VMoption。但仍然发生同样的错误。我认为阅读整个文件存在一些问题。下面的代码最多可用于300mb。请给我一些建议。提前致谢publicc…
    2025-04-161
  • Log4j 记录到共享日志文件

    Log4jLoggingtoaSharedLogFile有没有办法将log4j日志记录事件写入也被其他应用程序写入的日志文件。其他应用程序可以是非Java应用程序。有什么缺点?锁定问题?格式化?Log4j有一个SocketAppender,它将向服务发送事件,您可以自己实现或使用与Log4j捆绑的简单实现。它还支持syslogd和Windows事件日志,这对于尝试将日志输出与来自非Java应用程序…
    2025-04-161