Can we declare SimpleDateFormat objects as static objects

Can we declare SimpleDateFormat objects as static objects

SimpleDateFormat monthFormat = new SimpleDateFormat(“MMMM”);
SimpleDateFormat fullFormat = new SimpleDateFormat(“EE MMM dd, HH:mm:ss”)

I have several such piece of code which gets invoked often, would it make sense to declare them as static variables?
Is it thread safe to pass dynamic arguments to the format() method in such cases?

Solutions/Answers:

Solution 1:

No they aren’t thread-safe.Use Joda-time’s version instead.

Or make them wrapped in synchronized method and make it thread-safe

Doc Says it clearly

Date formats are not synchronized. It
is recommended to create separate
format instances for each thread. If
multiple threads access a format
concurrently, it must be synchronized
externally.

Solution 2:

As of Java 8, this is supported in the new Date API. DateTimeFormatter is thread-safe and can do the same work as SimpleDateFormat. Cited from the JavaDoc:

A formatter created from a pattern can be used as many times as necessary, it is immutable and is thread-safe.

To be extra clear, it is perfectly fine to define a format such as:

private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");

And use it in methods that can be accessed by several threads concurrently:

String text = date.toString(formatter);
LocalDate date = LocalDate.parse(text, formatter);

Solution 3:

DateFormat is not thread-safe. If multiple threads use the same DateFormat object without any synchronization you can get unexpected results. So you should either synchronize access to the DateFormat object, use a ThreadLocal variable or use an alternative Date API such as Joda-Time.

For more information on how to do this, take a look at this blog post: DateFormat with Multiple Threads

Solution 4:

An alternative if you are already using Apache Commons:

https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/time/FastDateFormat.html

Solution 5:

static shouldn’t be a problem.

Since AFAIK no guarantees are made about thread safety you’d have to check the source code for that. And even if you come to the conclusion that it is thread safe, this might change with the next release. As said in another answer they are not thread safe.

Do you really allocate such a huge amount of threads that giving each thread its own Format is a problem?

References

Remove Top-Level Container on Runtime

Remove Top-Level Container on Runtime

Unfortunately, it looks like this recently closed question was not well understood. Here is the typical output:
run:
Trying to Remove JDialog
Remove Cycle Done πŸ™‚
Checking if still exists any of TopLayoutContainers
JFrame
JDialog
Will Try Remove Dialog again, CycleNo. 1
———————————————————–
Trying to Remove JDialog
Remove Cycle Done πŸ™‚
Checking if still exists any of TopLayoutContainers
JFrame
JDialog
Will Try Remove Dialog again, CycleNo. 2
———————————————————–
Trying to Remove JDialog
Remove Cycle Done πŸ™‚
Checking if still exists any of TopLayoutContainers
JFrame
JDialog
Will Try Remove Dialog again, CycleNo. 3
———————————————————–
Trying to Remove JDialog
Remove Cycle Done πŸ™‚
Checking if still exists any of TopLayoutContainers
JFrame
JDialog
*** End of Cycle Without Success, Exit App ***
BUILD SUCCESSFUL (total time: 13 seconds)

I’ll try asking this question again: How can I kil*l on Runtime the first-opened top-Level Container, and help with closing for me one of Swing NightMares?
import java.awt.*;
import java.awt.event.WindowEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class RemoveDialogOnRuntime extends JFrame {

private static final long serialVersionUID = 1L;
private int contID = 1;
private boolean runProcess;
private int top = 20;
private int left = 20;
private int maxLoop = 0;

public RemoveDialogOnRuntime() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300, 300));
setTitle(“Remove Dialog On Runtime”);
setLocation(150, 150);
pack();
setVisible(true);
Point loc = this.getLocation();
top += loc.x;
left += loc.y;
AddNewDialog();
}

private void AddNewDialog() {
DialogRemove firstDialog = new DialogRemove();
remWins();
}

private void remWins() {
runProcess = true;
Thread th = new Thread(new RemTask());
th.setDaemon(false);
th.setPriority(Thread.MIN_PRIORITY);
th.start();
}

private class RemTask implements Runnable {

@Override
public void run() {
while (runProcess) {
Window[] wins = Window.getWindows();
for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JDialog) { System.out.println(" Trying to Remove JDialog"); wins[i].setVisible(false); wins[i].dispose(); WindowEvent windowClosing = new WindowEvent(wins[i], WindowEvent.WINDOW_CLOSING); wins[i].dispatchEvent(windowClosing); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing); Runtime runtime = Runtime.getRuntime(); runtime.gc(); runtime.runFinalization(); } try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(RemoveDialogOnRuntime.class.getName()).log(Level.SEVERE, null, ex); } } wins = null; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { System.out.println(" Remove Cycle Done :-)"); Runtime.getRuntime().runFinalization(); Runtime.getRuntime().gc(); runProcess = false; } }); } pastRemWins(); } } private void pastRemWins() { System.out.println(" Checking if still exists any of TopLayoutContainers"); Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JFrame) { System.out.println("JFrame"); wins[i].setVisible(true); } else if (wins[i] instanceof JDialog) { System.out.println("JDialog"); wins[i].setVisible(true); } } if (wins.length > 1) {
wins = null;
maxLoop++;
if (maxLoop <= 3) { System.out.println(" Will Try Remove Dialog again, CycleNo. " + maxLoop); System.out.println(" -----------------------------------------------------------"); remWins(); } else { System.out.println(" -----------------------------------------------------------"); System.out.println("*** End of Cycle Without Success, Exit App ***"); closeMe(); } } } private void closeMe() { EventQueue.invokeLater(new Runnable() { @Override public void run() { System.exit(0); } }); } private class DialogRemove extends JDialog { private static final long serialVersionUID = 1L; DialogRemove(final Frame parent) { super(parent, "SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } private DialogRemove() { setTitle("SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } } public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { @Override public void run() { RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime(); } }); } }

Solutions/Answers:

Solution 1:

Invoking dispose() allows the host platform to reclaim memory consumed by the heavyweight peer, but it can’t do so until after the WINDOW_CLOSING event is processed on the EventQueue. Even then, gc() is a suggestion.

Addendum: Another way to see the nightmare is via a profiler. Running the example below with jvisualvm, one can see that periodic collection never quite returns to baseline. I’ve exaggerated the vertical axis by starting with an artificially small heap. Additional examples are shown here. When memory is very limited, I’ve used two approaches:

  • Emergent: Loop from the command line, starting a new VM each time.

  • Urgent: Eliminate the heavyweight component entirely, running headless and composing in a BufferedImage using 2D graphics and lightweight components only.

enter image description here

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;

/** @see https://stackoverflow.com/questions/6309407 */
public class DialogClose extends JDialog {

    public DialogClose(int i) {
        this.setTitle("Dialog " + String.valueOf(i));
        this.setPreferredSize(new Dimension(320, 200));
    }

    private void display() {
        this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        passSomeTime();
        this.setVisible(false);
        this.dispatchEvent(new WindowEvent(
            this, WindowEvent.WINDOW_CLOSING));
        this.dispose();
        passSomeTime();
    }

    private void passSomeTime() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException ie) {
            ie.printStackTrace(System.err);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                int count = 0;
                while (true) {
                    new DialogClose(count++).display();
                }
            }
        });
    }
}

Solution 2:

I have completely reworked your example:

  • I have simplified what was not needed (setLocation(), unused constructor…)
  • I have removed the code that triggers a WINDOW_CLOSING event (useless)
  • I have removed code that resets all windows to visible again (which would prevent GC on them)
  • I have used a javax.swing.Timer instead of a Thread for disposing of the dialog
  • I have used a Thread for forcing GC (not a good idea in the EDT)
  • I have modified the final success criterion to check that Window.getWindows() is 2 (not 1), because in Swing, if you open a dialog with no parent, then a special invisible frame will be created to use it as parent (for all ownerless dialogs actually), once created, that frame cannot be removed.

The resulting snippet follows:

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

public class RemoveDialogOnRuntime extends JFrame {

    private static final long serialVersionUID = 1L;
    private boolean runProcess;
    private int maxLoop = 0;
    private Timer timer;

    public RemoveDialogOnRuntime() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(300, 300));
        setTitle("Remove Dialog On Runtime");
        setLocation(150, 150);
        pack();
        setVisible(true);
        addNewDialog();
    }

    private void addNewDialog() {
        DialogRemove firstDialog = new DialogRemove();
        remWins();
    }

    private void remWins() {
        runProcess = true;
        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (runProcess) {
                    for (Window win: Window.getWindows()) {
                        if (win instanceof JDialog) {
                            System.out.println("    Trying to Remove JDialog");
                            win.dispose();
                        }
                    }
                    System.out.println("    Remove Cycle Done :-)");
                    runProcess = false;
                    new Thread() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            Runtime.getRuntime().gc();
                        }
                    }.start();
                } else {
                    pastRemWins();
                    runProcess = true;
                }
            }
        });
        timer.setRepeats(true);
        timer.start();
    }

    private void pastRemWins() {
        System.out.println("    Checking if still exists any of TopLayoutContainers");
        Window[] wins = Window.getWindows();
        for (int i = 0; i < wins.length; i++) {
            if (wins[i] instanceof JFrame) {
                System.out.println("JFrame");
            } else if (wins[i] instanceof JDialog) {
                System.out.println("JDialog");
            } else {
                System.out.println(wins[i].getClass().getSimpleName());
            }
        }
        // We must expect 2 windows here: this (RemoveDialogOnRuntime) and the parent of all parentless dialogs
        if (wins.length > 2) {
            wins = null;
            maxLoop++;
            if (maxLoop <= 3) {
                System.out.println("    Will Try Remove Dialog again, CycleNo. " + maxLoop);
                System.out.println(" -----------------------------------------------------------");
                remWins();
            } else {
                System.out.println(" -----------------------------------------------------------");
                System.out.println("*** End of Cycle Without Success, Exit App ***");
                closeMe();
            }
        } else {
            timer.stop();
        }
    }

    private void closeMe() {
        System.exit(0);
    }

    private class DialogRemove extends JDialog {

        private static final long serialVersionUID = 1L;

        private DialogRemove() {
            setTitle("SecondDialog");
            setPreferredSize(new Dimension(200, 200));
            setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            setModalityType(Dialog.ModalityType.MODELESS);
            pack();
            setVisible(true);
        }
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime();
            }
        });
    }
}

The important conclusions are:

  • You can’t remove the invisible frame created by Swing as parent of all ownerless dialogs
  • You have to force a GC for the disposed dialog to be removed from Window.getWindows() (that one looks like a bug to me but I think the reason is that Swing keeps a WeakReference to all windows, and this WeakReference is not released until a GC has occurred.

Hope this gives a clear and complete answer to your problem.

Solution 3:

with the intent to blow away all doubts about EDT and confirm trashgod Updated suggestion, then output to the console is

run:
7163 KB used before GC
    Trying to Remove JDialog
    Remove Cycle Done :-)
405 KB used after GC
    Checking if still exists any of TopLayoutContainers
JFrame
JDialog
    Will Try Remove Dialog again, CycleNo. 1
 -----------------------------------------------------------
3274 KB used before GC
    Trying to Remove JDialog
    Remove Cycle Done :-)
403 KB used after GC
    Checking if still exists any of TopLayoutContainers
JFrame
JDialog
    Will Try Remove Dialog again, CycleNo. 2
 -----------------------------------------------------------
3271 KB used before GC
    Trying to Remove JDialog
    Remove Cycle Done :-)
406 KB used after GC
    Checking if still exists any of TopLayoutContainers
JFrame
JDialog
    Will Try Remove Dialog again, CycleNo. 3
 -----------------------------------------------------------
3275 KB used before GC
    Trying to Remove JDialog
    Remove Cycle Done :-)
403 KB used after GC
    Checking if still exists any of TopLayoutContainers
JFrame
JDialog
 -----------------------------------------------------------
*** End of Cycle Without Success, Exit App ***
BUILD SUCCESSFUL (total time: 26 seconds) 

from code

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

public class RemoveDialogOnRuntime extends JFrame {

    private static final long serialVersionUID = 1L;
    private int contID = 1;
    private boolean runProcess;
    private int top = 20;
    private int left = 20;
    private int maxLoop = 0;
    private javax.swing.Timer timer = null;

    public RemoveDialogOnRuntime() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(300, 300));
        setTitle("Remove Dialog On Runtime");
        setLocation(150, 150);
        pack();
        setVisible(true);
        Point loc = this.getLocation();
        top += loc.x;
        left += loc.y;
        AddNewDialog();
    }

    private void AddNewDialog() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                DialogRemove firstDialog = new DialogRemove();
                startAA();
            }
        });
    }

    private void startAA() {
        timer = new javax.swing.Timer(5000, updateAA());
        timer.setRepeats(false);
        timer.start();
    }

    public Action updateAA() {
        return new AbstractAction("text load action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                timer.stop();
                if (SwingUtilities.isEventDispatchThread()) {
                    Runnable doRun = new Runnable() {

                        @Override
                        public void run() {
                            remWins();
                        }
                    };
                    SwingUtilities.invokeLater(doRun);
                } else {
                    Runnable doRun = new Runnable() {

                        @Override
                        public void run() {
                            remWins();
                        }
                    };
                    SwingUtilities.invokeLater(doRun);
                }
            }
        };
    }

    private void remWins() {
        Runtime runtime = Runtime.getRuntime();
        long total = runtime.totalMemory();
        long free = runtime.freeMemory();
        long max = runtime.maxMemory();
        long used = total - free;
        System.out.println(Math.round(used / 1e3) + " KB used before GC");
        Window[] wins = Window.getWindows();
        for (int i = 0; i < wins.length; i++) {
            if (wins[i] instanceof JDialog) {
                System.out.println("    Trying to Remove JDialog");
                wins[i].setVisible(false);
                wins[i].dispose();
                WindowEvent windowClosing = new WindowEvent(wins[i], WindowEvent.WINDOW_CLOSING);
                wins[i].dispatchEvent(windowClosing);
                Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing);
                runtime = Runtime.getRuntime();
                runtime.gc();
                runtime.runFinalization();
            }
        }
        wins = null;
        System.out.println("    Remove Cycle Done :-)");
        runtime.runFinalization();
        runtime.gc();
        runtime = Runtime.getRuntime();
        total = runtime.totalMemory();
        free = runtime.freeMemory();
        max = runtime.maxMemory();
        used = total - free;
        System.out.println(Math.round(used / 1e3) + " KB used after GC");
        startOO();
    }

    private void startOO() {
        timer = new javax.swing.Timer(5000, updateOO());
        timer.setRepeats(false);
        timer.start();
    }

    public Action updateOO() {
        return new AbstractAction("text load action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                timer.stop();
                timer.stop();
                if (SwingUtilities.isEventDispatchThread()) {
                    Runnable doRun = new Runnable() {//really contraproductive just dealayed

                        @Override
                        public void run() {
                            pastRemWins();
                        }
                    };
                    SwingUtilities.invokeLater(doRun);
                } else {
                    Runnable doRun = new Runnable() {

                        @Override
                        public void run() {
                            pastRemWins();
                        }
                    };
                    SwingUtilities.invokeLater(doRun);
                }
            }
        };
    }

    private void pastRemWins() {
        System.out.println("    Checking if still exists any of TopLayoutContainers");
        Window[] wins = Window.getWindows();
        for (int i = 0; i < wins.length; i++) {
            if (wins[i] instanceof JFrame) {
                System.out.println("JFrame");
                wins[i].setVisible(true);
            } else if (wins[i] instanceof JDialog) {
                System.out.println("JDialog");
                wins[i].setVisible(true);
            }
        }
        if (wins.length > 1) {
            wins = null;
            maxLoop++;
            if (maxLoop <= 3) {
                System.out.println("    Will Try Remove Dialog again, CycleNo. " + maxLoop);
                System.out.println(" -----------------------------------------------------------");
                remWins();
            } else {
                System.out.println(" -----------------------------------------------------------");
                System.out.println("*** End of Cycle Without Success, Exit App ***");
                closeMe();
            }
        }
        startAA();
    }

    private void closeMe() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                System.exit(0);
            }
        });
    }

    private class DialogRemove extends JDialog {

        private static final long serialVersionUID = 1L;

        DialogRemove(final Frame parent) {
            super(parent, "SecondDialog " + (contID++));
            setLocation(top, left);
            top += 20;
            left += 20;
            setPreferredSize(new Dimension(200, 200));
            setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            setModalityType(Dialog.ModalityType.MODELESS);
            pack();
            setVisible(true);
        }

        private DialogRemove() {
            setTitle("SecondDialog " + (contID++));
            setLocation(top, left);
            top += 20;
            left += 20;
            setPreferredSize(new Dimension(200, 200));
            setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            setModalityType(Dialog.ModalityType.MODELESS);
            pack();
            setVisible(true);
        }
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime();
            }
        });
    }
}

Solution 4:

I’m not sure if you question is about “garbage collection” or about how to identify dialogs that are visible.

You can’t control when garbage collection is done. Invoking the gc() method is only a suggestion.

If you want to ignore “disposed” dialogs then you can use the isDisplayable() method to check its status.

With the following program I got some interesting results. First change I made was to add some components to the dialog so that more resources would be used for each dialog which would increase the chance that the resources would be garbage collected.

On my machine I found that if I

a) create 5 dialogs
b) close the dialogs
c) create 5 dialogs

Then the first 5 appear to be garbage collected.

However if I create 5, then close then create 1, then close, it doesn’t seem to work.

Bottom line is you can’t depend on when garbage collection will be done, so I suggest you use the isDisplayable() method to determine how to do your processing. The “Display Dialogs” button uses this method as part of the displayed output.

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

public class DialogSSCCE extends JPanel
{
    public static int count;

    public DialogSSCCE()
    {
        JButton display = new JButton("Display Dialogs");
        display.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println("Display Dialogs");

                for (Window window: Window.getWindows())
                {
                    if (window instanceof JDialog)
                    {
                        JDialog dialog = (JDialog)window;
                        System.out.println("\t" + dialog.getTitle() + " " + dialog.isDisplayable());
                    }
                }
            }
        });
        add( display );

        JButton open = new JButton("Create Dialog");
        open.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println("Create Dialog");

                JDialog dialog = new JDialog();
                dialog.getContentPane().setLayout(null);

                for (int i = 0; i < 200; i++)
                {
                    dialog.add( new JTextField("some text") );
                }

                dialog.setTitle("Dialog " + count++);
                dialog.setLocation(count * 25, count * 25);
                dialog.setVisible(true);
                System.out.println("\tCreated " + dialog.getTitle());
            }
        });
        add( open );

        JButton close = new JButton("Close Dialogs");
        close.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println("Close Dialogs");

                for (Window window: Window.getWindows())
                {
                    if (window instanceof JDialog)
                    {
                        JDialog dialog = (JDialog)window;
                        System.out.println("\tClosing " + dialog.getTitle());
                        dialog.dispose();
                    }
                }

                Runtime.getRuntime().gc();
            }
        });
        add( close );
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("DialogSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new DialogSSCCE() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

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

Solution 5:

There is a timeout defined in the AppContext before some resources will be released finally. This is set to something like 5 seconds. Thus if you wait for another five seconds also the context will dispose the (last) reference to your dialog.

wins = null;
Thread.sleep(5000);

References