Hi im writting a very simple game. Player can use mouse to move spaceship and every 200ms new beam is shoot. This beam is moved in while(true) loop and when its y is 0 or 400 (bounds of frame) i use break to end the loop (and thread). Every beam has its own thread. There are also stars which move in background. Every of them moves like beams and has its own thread. So as you can see there are often add and removes from arrayLists. Everything works but from time to time I get such errors:
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at spacecommander.MainPanel.paintComponent(MainPanel.java:50)
They doesnt make any problems in game but how can I eliminate them? Maybe I should use synchronization or something?
EDIT: HERE IS THE CODE
public class MainPanel extends JPanel {
private Player player = new Player(100, 100, 3, 3);
private Point2D targetPoint = new Point2D.Float(130, 350); //Poczatkowa pozycja statku
private ArrayList<Beam> beams = new ArrayList<Beam>();
private InputMap imap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
private ActionMap amap = getActionMap();
private Random rand = new Random();
public MainPanel() {
setPreferredSize(new Dimension(300, 400));
addMouseMotionListener(new MouseMotionHandler());
//Rozpoczynanie watkow
Thread t = new Thread(new PlayerMoveRunnable());
t.start();
Thread t2 = new Thread(new PlayerShootRunnable());
t2.start();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, 300, 400);
//Rysowanie gracza
g2.drawImage(player.getImage(), (int)player.getX(), (int)player.getY(), null);
//Rysowanie pociskow
for (Beam beam : beams) {
g2.drawImage(beam.getImage(), (int)beam.getX(), (int)beam.getY(), null);
}
}
public void makeShortcut(String name, String keys, AbstractAction action) {
imap.put(KeyStroke.getKeyStroke(keys), name);
amap.put(name, action);
}
//Watek dziala caly czas bo gracz i tak caly czas sie rusza
private class PlayerMoveRunnable implements Runnable {
public void run() {
try {
while (true) {
player.moveToPoint(targetPoint);
repaint();
Thread.sleep(15);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//Takze dziala caly czas. Dodaje nowy pocisk co 200ms
private class PlayerShootRunnable implements Runnable {
public void run() {
try {
while (true) {
//Wybranie pocisku do wystrzelenia w zaleznosci od mode gracza
Thread t;
switch (player.getBeamMode()) {
case 1:
t = new Thread(new BeamMoveRunnable(new Beam1(100, 100, 10, 10, 10)));
break;
}
t.start();
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private class BeamMoveRunnable implements Runnable {
private Beam beam;
public BeamMoveRunnable(Beam beam) {
this.beam = beam;
}
public void run() {
Beam beam = this.beam;
beams.add(beam);
try {
while (true) {
if (beam.getY() <= 0) {
beams.remove(beam);
break;
}
beam.move();
repaint();
Thread.sleep(20);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private class MouseMotionHandler extends MouseAdapter {
public void mouseMoved(MouseEvent event) {
targetPoint = event.getPoint();
}
}
}
This seems to be a synchronization issue, as you suspected. Probably while your drawing code is iterating the beams-list, a BeamMoveRunnable modifies the list at the same time (either adding or removing a beam), and causes the ConcurrentModificationException. Personally I wouldn't use separate threads to move the beams, but a simple loop which first updates the game (moving all the beams one at a time etc.) and then redraws the screen. However, you can also just synchronize the access to the list so only a single thread can access it at a time.
Since you are using multiple threads different threads are trying to modify your beams arraylist. You can use synchronized block to avoid concurent modification exception as below
Looping the list
synchronized (beams) {
for (Iterator it = beams.iterator(); it.hashNext(); ) {
Beam beam = (Beam) it.next();
g2.drawImage(beam.getImage(), (int)beam.getX(), (int)beam.getY(), null);
}
}
Adding item to the list
syncronized (beams) {
beams.add(beam);
}
removing item from the list
syncronized (beam) {
list.remove(beam);
}
This typically happens when you try to add or remove an item from a list while iterating:
for (String s : list) {
list.add("abc"); //ConcurrentModificationException
}
It is difficult to be more specific without seeing the code that is around line 50 of your MainPanel class (cf. stacktrace: at spacecommander.MainPanel.paintComponent(MainPanel.java:50)).
EDIT
Following your edit, a simple change would be to use a CopyOnWriteArrayList which is thread safe, instead of an ArrayList, to hold your Beam objects.
Related
So I have been looking to update one of my panels in a my client code with data that comes from a server in Indonesia. The delay is rather long (2-8) sec and Im noticing that my UI is freezing during the time it takes for the response to return from the server.
The response will be used to draw some points on a map (not yet implemented).
I have been looking all over the net to find out how to do it and I have come across:
InvokeLater.
SwingWroker.
ScheduledThreadPoolExecutor.
Making the JPanel a runnable to run in its own thread(seems like best option).
http://www.java2s.com/Tutorial/Java/0160__Thread/CreateathreadtoupdateSwing.htm
But tbh most of the data i find is out dated (more than 5 years old).
Here is the JPanel class i want to update based on a server query:
public class MapPanel extends JPanel implements Pointable, Runnable {
private static final long serialVersionUID = 1L;
private List<Shape> shapes = new LinkedList<>();
private State mapPanelState;
public Shape selected;
private BufferedImage image;
public MapPanel() {
Commander.getInstance().addShapeContainer(this);
mapPanelState = NoState.getInstance();
MouseHandler mouseHandler = new MouseHandler(this);
KeyListener keyListener = new KeyListener();
readImage();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
this.addKeyListener(keyListener);
this.setBackground(Color.white);
this.setFocusable(true);
this.requestFocusInWindow();
}
private void readImage(){
try {
image = ImageIO.read(new File("/MapCoordProject/earthmap1.jpg"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void setState(State state) {
mapPanelState = state;
}
public List<Shape> getShapes() { return shapes; }
public void setShapes(List<Shape> shapes) {
this.shapes = shapes;
}
public Shape getLastShape(){ return shapes.get(shapes.size()-1); }
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
for (Shape shape : shapes)
shape.draw((Graphics2D) g);
}
public void select(Point point) {
for (Shape shape : shapes) {
if (shape.intersects(point)) {
selected = shape;
}
}
}
#Override
public Dimension getPreferredSize() {
if (image == null) {
return super.getPreferredSize();
} else {
int w = image.getWidth();
int h = image.getHeight();
return new Dimension(w, h);
}
}
public void pointerDown(Point point) {
mapPanelState.pointerDown(point, this);
}
public void pointerUp(Point point) {
mapPanelState.pointerUp(point, this);
selected = null;
}
public void pointerMoved(Point point, boolean pointerDown) {
mapPanelState.pointerMoved(point, pointerDown, this);
}
#Override
public void run() {
}
}
I want a method that updates the "Shapes" array in a separate thread to stop everything from freezing.
Any suggestions?
Making your JPanel implement Runnable is not the best solution. There is no reason to expose a run() method to other classes.
Instead, create a private void method that takes no arguments. A method reference that refers to that method can act as a Runnable, since it will have the same arguments and return type. You can then pass it to a Thread constructor.
public MapPanel() {
// ...
readImage();
new Thread(this::readShapes, "Reading shapes").start();
// ...
}
private void readShapes() {
try {
List<Shape> newShapes = new ArrayList<>();
URL server = new URL("https://example.com/indonesia/data");
try (InputStream dataSource = server.openStream()) {
while ( /* ... */ ) {
Shape shape = /* ... */;
newShapes.add(shape);
}
}
EventQueue.invokeLater(() -> setShapes(newShapes));
} catch (IOException e) {
e.printStackTrace();
EventQueue.invokeLater(() -> {
JOptionPane.showMessageDialog(getTopLevelContainer(),
"Unable to retrieve data:\n" + e, "Load Error",
JOptionPane.ERROR_MESSAGE);
});
}
}
Notice that calls to methods involving Swing objects are always wrapped in a call to EventQueue.invokeLater, to make sure they run on the correct thread.
It is possible to improve this by creating a progress dialog that shows while the data is being loaded, but that would make this answer much longer and would require more knowledge about the Indonesian API you’re calling.
I've been working on this for awhile and can't figure out why this applet is not working properly:
I instantiated two threads in my applet.
I created two buttons- start, and stop, which are supposed to change the flag values to end the while() loops in my threads. The applet is not responding to either button. Any suggestions, anyone? Thanks for your time!
This is the applet...
package prodcons;
import java.applet.Applet;
import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyTableSetting extends Applet {
Soup s; // we will show the soup bowl with the soup's alphabet pieces
int bowlLength = 150; // bowl's dimensions as variables in case we want to change it
int bowlWidth = 220;
int bowlX = 60;
int bowlY = 10;
Producer p1;
Consumer c1;
public void init(){
setSize(400,200); // make the applet size big enough for our soup bowl
s = new Soup(); // instantiate the Soup
p1 = new Producer(this, s); // declare and instantiate one producer thread - state of NEW
c1 = new Consumer(this, s); // declare and instantiate one consumer thread - state of NEW
p1.start(); // start the producer thread
c1.start(); // start the consumer thread
Button stop = new Button("Stop");
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = false;
Consumer.consumerRunning = false;
Soup.clearBuffer();
}
});
add(stop);
Button start = new Button("Start");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = true;
Consumer.consumerRunning = true;
p1.run();
System.out.println("heyyy");
c1.run();
}
});
add(start);
}
public void paint(Graphics g){ // first we make the bowl and spoon
int x;
int y;
g.setColor(Color.orange);
g.fillOval(bowlX, bowlY, bowlWidth, bowlLength); // the bowl
g.setColor(Color.cyan);
g.fillOval(10, 25, 40, 55); // the spoon
g.fillOval(25, 80, 8, 75);
g.setColor(Color.black); // black outlines for the dinnerware
g.drawOval(10, 25, 40, 55);
g.drawOval(25, 80, 8, 75);
g.drawOval(bowlX,bowlY, bowlWidth, bowlLength);
ArrayList <String> contents = s.getContents(); // get contents of the soup
for (String each: contents){ // individually add each alphabet piece in the soup
x = bowlX + bowlWidth/4 +(int)(Math.random()* (bowlWidth/2)); // put them at random places to mimic stirring
y = bowlY + bowlLength/4 + (int)(Math.random()* (bowlLength/2));
Font bigFont = new Font("Helvetica", Font.BOLD, 20);
g.setFont(bigFont);
g.drawString(each, x, y);
}
}
}
and these are the threads:
package prodcons;
class Consumer extends Thread {
private Soup soup;
private MyTableSetting bowlView;
static boolean consumerRunning = true;
public Consumer(MyTableSetting bowl, Soup s) {
bowlView = bowl; // the consumer is given the GUI that will show what is happening
soup = s; // the consumer is given the soup--the monitor
}
public void run() {
System.out.println("consuming: "+consumerRunning);
String c;
try {
while(consumerRunning) { // stop thread when know there are no more coming; here we know there will only be 10
c = soup.eat(); // eat it from the soup
System.out.println("Ate a letter: " + c); // show what happened in Console
bowlView.repaint(); // show it in the bowl
sleep((int)(Math.random() * 3000)); // have consumer sleep a little longer or sometimes we never see the alphabets!
}
} catch (InterruptedException e) {
this.interrupt();
}
}
}
and this:
package prodcons;
class Producer extends Thread {
private Soup soup;
private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private MyTableSetting bowlView;
static boolean producerRunning = true;
public Producer(MyTableSetting bowl, Soup s) {
bowlView = bowl; // the producer is given the GUI that will show what is happening
soup = s; // the producer is given the soup--the monitor
}
public void run() {
String c;
try {
while (producerRunning) { // only put in 10 things so it will stop
System.out.println("thikns producer != null");
c = String.valueOf(alphabet.charAt((int)(Math.random() * 26))); // randomly pick a number to associate with an alphabet letter
soup.add(c); // add it to the soup
System.out.println("Added " + c + " to the soup."); // show what happened in Console
bowlView.repaint(); // show it in the bowl
sleep((int)(Math.random() * 2000)); // sleep for a while so it is not too fast to see
}
} catch (InterruptedException e) {
this.interrupt();
}
}
}
and here is the "Soup" Class:
package prodcons;
import java.util.*;
public class Soup {
private static ArrayList <String> buffer = new ArrayList<String>(); // buffer holds what is in the soup
private int capacity = 6;
public synchronized String eat() { //this method can only be accessed by one thing at a time
while(buffer.isEmpty()){ // cannot eat if nothing is there, so check to see if it is empty
try {
wait(); // if so, we WAIT until someone puts something there
} catch (InterruptedException e) {} // doing so temporarily allows other synchronized methods to run (specifically - add)
} // we will not get out of this while until something is there to eat
String toReturn = buffer.get((int)(Math.random() * buffer.size())); // get a random alphabet in the soup
buffer.remove(toReturn); // remove it so no one else can eat it
buffer.trimToSize(); // reduce the size of the buffer to fit how many pieces are there
notifyAll(); // tell anyone WAITing that we have eaten something and are done
return(toReturn);
}
public synchronized void add(String c) {
while (buffer.size() == capacity) {
try {
wait();
}
catch (InterruptedException e) {}
}
buffer.add(c);
notifyAll();
}
public ArrayList <String> getContents() {
return buffer;
}
public static void clearBuffer() {
buffer.clear();
}
}
Thank you so much!
You call the Producer and Consumer directly on the main UI thread from the event handler for the start button. The only effect that this will have is to freeze your entire application. That's because the main UI thread does everything: event handling, repainting, etc. As long as you hold on to it, there is going to be no event handling, repainting, etc.
You should never call anything that takes long on the main UI thread.
But since you've already started the threads in your init method, you don't need to call run in your action listener. You remove those calls:
Button start = new Button("Start");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = true;
Consumer.consumerRunning = true;
}
});
Also, you should make the flags volatile, otherwise the change to them from one thread may not be visible in the other thread:
volatile static boolean producerRunning = true;
(etc.)
I am trying to move an object randomly. I have my GUI class which uses another class (lets say Obj) to create a new image and then start the thread to make the object move randomly. But my repaint() does not work in this context. The code below can give you an idea about how I am using the repaint method.
thanks,
Gui class
public class GUI extends JFrame implements ActionListener {
public void addNewObj(){
Obj f = new Obj();
x = panel.getGraphics();
f.paint(x);
Thread thr=new Thread(f);
thr.start();
}
}
Create object class
public class Obj extends JPanel implements Runnable
{
public Obj()
{
try {
myImage = ImageIO.read(new File("b:\\imgs\\bottle.jpg"));
}
catch (IOException e) {}
}
public void run()
{
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true)
{
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
try
{
moveRandom();
repaint();
Thread.sleep(1000);
}
catch (InterruptedException e)
{
System.out.println("interrupted");
}
beforeTime = System.currentTimeMillis();
}
}
The problem is very basic: Never paint an object in the way you do. You should add it to the frame or an container. This is also the reason, why repaint() doesn't work. Your object never makes it into the componenthierachy, and therefore repaint will only repaint this single object, but nothing else (including the frame, which should be repainted). Simply add the object directly to the frame, validate and repaint the frame.
the new addNewObj:
public void addNewObj(){
Obj f = new Obj();
Thread t = new Thread(f);
t.start();
panel.add(f);//add it to the panel
panel.validate();//validate the hierachy
panel.repaint();//repaint the whole thing to make the new obj visible
}
And override your Obj class to paint the objects:
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(myImage , 0 , 0 , Color.white , null);
}
I am new to programming (I'm 11 and hoping for java coding to be my career, but its just a hobby right now :)) and I just made a countdown program, here is the class:
package me.NoahCagle.JAVA;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 600;
public static int height = 500;
public static String title = "Countdown!";
public static boolean running = false;
public int number = 11;
public Thread thread;
Dimension size = new Dimension(width, height);
public Main() {
super(title);
setSize(size);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Main m = new Main();
m.start();
}
public void start() {
if (running) {
return;
}
running = true;
Thread thread = new Thread(this);
thread.start();
}
#SuppressWarnings("static-access")
public void run() {
while (running) {
number--;
if (number == -1) {
System.out.println("Done!");
System.exit(0);
}
try {
thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("" + number);
}
}
public void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
That may not have been necessary, but whatever. Well like I was saying, if you read the code, you will notice that it prints the value to the console. Well, if I could get that to display on a JLabel, while updating at the same time. I have tried just doing setText("" + number) thinking that because I have a thread going, it would repaint. But that didn't happen. It was just stuck at 11. Can someone please help me? Thanks
First, you may want to take a read through Concurrency in Swing. There are some very important constraints when it comes to dealing with multiple threads and Swing.
For your problem, you really should be using a javax.swing.Timer, and with examples...
Java Label Timer and Saving
Adding a timer and displaying label text
How could I add a simple delay in a Java Swing application?
As 11yrs old you have done good job here. But where did you add any panel to the frame on which u want to show the number? Once you do it and put some label to add the number you will need to call the repaint method. Also to use threads with swings, there are many libraries you can use like Timer.
Happy Coding!
First code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class cos {
public static int a;
private static JLabel labeler;
// public static Runnable r1;
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
a = 0;
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
cos window = new cos();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public cos() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
public void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 205, 194);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel lblTime = new JLabel("Time:");
frame.getContentPane().add(lblTime, BorderLayout.WEST);
final JLabel labeler = new JLabel("");
frame.getContentPane().add(labeler, BorderLayout.CENTER);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
Runnable r1 = new Runnable() {
public void run() {
while (a <= 10) {
a = a + 1;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
public void actionPerformed(ActionEvent arg0) {
Thread threder = new Thread(r1);
threder.start();
// liczniczek bla = new liczniczek();
}
});
frame.getContentPane().add(btnNewButton, BorderLayout.SOUTH);
}
public void licznik() {
while (a < 60) {
a = a + 1;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
And now my question. I wanna use code like this:
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
But that doesen't work. What i must do to separate this code ? Sorry for my bad english
Sierran.
never use Thread#sleep(int) during EDT, sure if is there only this thread then works correctly (with blockng EDT),
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
is wrong than same as you call plain licznik();, you have to wrap that this way
Runnable r1 = new Runnable(){
public void run(){
labeler.setText(Integer.toString(a));
}
};
but again without Thread#sleep(int), you have three choises
1) change Thread to the javax.swing.Timer
2) change Thread to the Runnable#Thread, there you can delaying with Thread#sleep(int), but output to the GUI must be
Runnable r1 = new Runnable(){
public void run(){
labeler.setText(Integer.toString(a));
}
};
3) use SwingWorker, where output is in the EDT and you can use Thread#sleep(int) too
example Thread#sleep(int) during EDT
put all together
EDIT
don't use reserved words as class, method, variable, whatever Name in the Programing languages (meaning cos)
your code works by implements all three options that I post here,
What do you mean "it doesn't work"? It works for me. How are you trying to use this code, and what errors or problems are you having when you run it? Myself, I'd use a SwingWorker though and I'd set the JLabel's text via the SwingWorker's publish/process method pair. To learn more on how to use this, please see this tutorial: Concurrency in Swing
Edit
Actually, an easier way to accomplish what you want is to not use threads or Runnables directly at all but to use a Swing Timer as they're built for just this case. For more on this, please check out the Swing Timer Tutorial
I gather that you want the function licznik() to run in a separate thread. You create a Runnable, but you have to do something more to make its run() method execute. There are a couple of ways to do this:
Runnable r1 = new Runnable(){
public void run(){
licznik();
}
};
new Thread(r1).start();
or you can just subclass Thread directly:
Thread r1 = new Thread(){
public void run(){
licznik();
}
};
r1.start();
Runnable interface has no method licznik(). You can create class that implements Runnable with licznik() method.
Or if you do not need to reuse this method and use it just once, then the fastest way is to move its implementation inside new Runnable() block
Runnable r1 = new Runnable(){
public void run(){
this.licznik();
}
public void licznik(){
while (a < 60){
a = a + 1 ;
labeler.setText(Integer.toString(a));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Look on GitHub under at https://github.com/greggwon/Ham. Look at the source code in https://github.com/greggwon/Ham/blob/master/SwingUtil/src/org/wonderly/swing/ComponentUpdateThread.java to see how I've packaged this whole detail into a single class which uses anonymous inner classes to do the work. It would be possible to change this to lambdas now, but I have not used Java in several years and thus haven't made that change.
new ComponentUpdateThread( new Action[] { add, del, edit } ) {
public void setup() {
super.setup();
list.setEnabled(false);
list.clearSelection();
}
public Object construct() {
try {
Vector v = remote.getData();
Collections.sort( v );
return v;
} catch( Exception ex ) {
reportException(ex);
}
return null;
}
public void finished() {
try {
Vector v = (Vector)getValue();
if( v != null ) list.setListData(v);
} finally {
super.finished();
list.setEnabled(true);
edit.setEnabled(false);
del.setEnaled(false);
}
}
}.start();
With this style of work, you can use final values from surrounding blocks or other class visible data to control various aspects of what happens before, during and after background thread execution.
I've change this code around over the years in various ways and there are other variations of this that exist.
The arguments to the ComponentUpdateThread constructor are controls/actions to be "disabled" while the background thread is running. Other enable/disable activities can be more literally embedded into the activities in setup() and finished() (which are run in the AWT event thread) before "construct" is run in the background thread.