Alright, I think to have figured this out enough, to be able to create an answer that might help you (for sure) and hopefully other people.
There is indeed multithreading involved, because
object.function2();
is creating a JFrame f. All drawing operations inside that frame f are executed by an AWT-EventQueue-0 (or any other number) task T. This task T is run parallel to your main thread M. Meaning every datastructure l (like a list) that is drawn in a custom JPanel p inside f and altered by M will potentially cause problems.
Example
public static void main(String[] args) {
List<Integer> l = new ArrayList<Integer>(5);
JFrame gui = new JFrame();
gui.add(new MyPanel());
l.add(10);
}
public class GUI extends JPanel {
private List<Integer> l;
public MyPanel(List<Integer> l) {
this.l = l;
}
@Override
public void paintComponent(Graphics g) { // this is called by JFrame.paint()
super.paintComponent(g);
List<Integer> modifiedL = new ArrayList<Integer>(l.size());
for(Integer i : l) {
modifiedL.add(2 * i);
}
// this is a stupid example, because it makes no sense to
// use this loop with l.size() here, but it shows the main problem.
for(int c = 0; c < l.size(); c++) {
somehowDrawSthWith(modifiedL.get(c));
}
}
}
By now changing the size of l after making the call to create the JFrame we cause an ArrayOutOfBoundsException when trying to access modifiedL.get(5), because the modifiedList may be created before l.add(10).
(A possible) Solution
Note that I wrote possible, because this is hacky and just be avoided if possible.
We need the Main thread M to wait until everything is painted. Here is a way to do this:
Java wait for JFrame to finish
In our example we would can change the main to:
public static void main(String[] args) {
List<Integer> l = new ArrayList<Integer>(5);
CountDownLatch jFrameDrawing = new CountDownLatch(1);
JFrame gui = new JFrame();
gui.add(new MyPanel(), jFrameDrawing); // add the counter
try {
jFrameDrawing.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
l.add(10);
}
And count down at the end of the drawing method like this
@Override
public void paintComponent(Graphics g) { // this is called by JFrame.paint()
super.paintComponent(g);
// ...
jFrameDrawing.countDown();
}
Now note, that this method might be called multiple times before p is really fully drawn. The reason is the following
Is the paint function in java graphics called multiple times when I add a JComponent?
So if your main looks like this:
public static void main(String[] args) {
List<Integer> l = new ArrayList<Integer>(5);
CountDownLatch jFrameDrawing = new CountDownLatch(3); // NOTE 3
JFrame gui = new JFrame();
gui.add(new MyPanel(), jFrameDrawing); // draw once
gui.setSize(100,50); // draw again
gui.setVisible(true); // draw a third time
try {
jFrameDrawing.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
l.add(10);
}
You need to set the initial counter to 3.
TL;DR:
Using AWT classes (e.g. JFrame) in
object.function2();
will create multithreading. This might create race conditions, like lists being modified concurrently.
See Java wait for JFrame to finish on how to think the drawing with the main code.