0

Hey I am modifying the class called SimpleBalls from this answer (the lower one). In the original version all the items in the list are being added by ballsUp.add(new Ball() before the animation takes place.

However, I want to dynamically add balls depending on some console output happening in another thread. This, of course, leads to comodification as the animation methods and me adding Balls will often access the list at the same time.

I tried setting my list to Collections.synchronizedList but I think it only had an effect to the "upper part" of the program as, just above the BounceEngine class we have

public List<Ball> getBalls() {
            return ballsUp;
        }

set up for the iterations and I don't really know how to apply this there. Also, the same applies to synchronized(list) as neither ballsUp (which is the "real" List name), Ball nor getBalls() can be passed as an argument to synchronized().

This is how I am adding new Balls:

  Runnable r = new Runnable() {
            public void run() {

          String s = null;
             try {
              while ((s = stdInput.readLine()) != null) {
               if(s.startsWith("sttest")){
                ballsUp.add(new Ball(new Color(random(200)+54, random(200)+54, random(200)+54)));
                           }
                    }
            }
        };

        new Thread(r).start();

Removing the ballsUp.add makes the code code seamlessly.

The rest of the class is pretty much the same, the only major change is the Thread calling the add.

UPDATE: I was able to narrow down the problem a bit. No matter with what I "wrap" it, the add won't work if inside the while ((s = stdInput.readLine()) != null). Even when I only run this add once the ball still won't appear. If I place it below or above the while statement it works perfectly fine, the thread itself or the try/catch doesn't seem to be the error, it's something with the while loop (or its condition)

What triggers the error now is the line shown in the screenshot enter image description here

  • but only if the ballsUp.add(new Ball() is under the hood of the while loop

enter image description here

Also, SwingUtilities.invokeLater applied to the for loop in the upper picture results in no ball being drawn at all.

Community
  • 1
  • 1
user2875404
  • 3,048
  • 3
  • 25
  • 47

2 Answers2

1

Just make sure the list is always accessed from a single thread: the event dispatch thread:

if (s.startsWith("sttest")) {
    SwingUtilities.invokeLater(() -> ballsUp.add(new Ball(new Color(random(200)+54, random(200)+54, random(200)+54))));
}

You wouldn't have to care about that (because it would happen automatically), if you added a ball to the list as a consequence of a UI event (like a button click): the event would be handled from the event dispatch thread directly.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks so much man! Applied it to (Ball ball : getParent().getBalls()) and the balls still don't disappear but I think that's an error I made somewhere else, thanks a lot! – user2875404 Dec 16 '15 at 17:18
  • hey mate I updated the question, maybe you know something :/ – user2875404 Dec 16 '15 at 19:38
  • I have no idea what you're doing here. Why don't you do what my answer tells you to do? I can't understand your picture, and I can't understand why you're trying to call paint on your balls inside an invokeLater. – JB Nizet Dec 16 '15 at 19:42
  • I did this for testing because using invokeLater() only on the ballsUp.add(new Ball()) leads to no ball appearing at all. The class gets called, I put a print there and it works fine but the repaint doesn't seem to acknowledge the object || again, it works when put outside of the `while`. Even when I make the ballsUp.add(new Ball() run only once in the `while` it still doesn't run properly, might the invokeLater() block the "creation" of the ball? – user2875404 Dec 17 '15 at 17:11
  • ok bro nvm found the error. Sorry for wasting your time, will edit soon but I will keep the tick on your answer as you did most of it! – user2875404 Dec 17 '15 at 19:52
0

You could return a copy of the list:

public List<Ball> getBalls() {
    return new ArrayList(ballsUp);
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213