Dyn. Schneefall (Synchronisationsprobleme: Erledigt)

darkeye2

Bekanntes Mitglied
Hallo,
ich nutze mehrere Listen, die ich auf folgende weiße erstelle:
Java:
	private volatile List<Dimension> size = Collections.synchronizedList(new ArrayList<Dimension>());
	private volatile List<Point> position = Collections.synchronizedList(new ArrayList<Point>());
	private volatile List<Integer> speed = Collections.synchronizedList(new ArrayList<Integer>());

Nun habe ich einen Thread, der in unbestimmten abständen (zufällig) neue elemente hinzufügt und einen anderen Thread, der alle 24ms (zeit kann sich aber ändern, sollte für das problem eigentlich auch nicht relevant sein) werte aus den arrays holt und diese auch je nach situation verändert. Das verändern erfolgt durch eine for each schleife (for(Point pos:position){...}) und leider kriege ich genau bei dieser Schleife einen java.util.ConcurrentModificationException , nun weiß ich nicht, wie ich das lösen soll, hatte es vorher mit normaler ArrayList und synchronized blöcke versucht, was aber zum gleichen Fehler geführt hat.
 
Zuletzt bearbeitet:
S

SlaterB

Gast
synchronizedList() kann nur einzelne Methoden wie add() oder get() abdecken, der Schleifendurchlauf ist eine lange Aktion mit vielen Einzelaufrufen, zwischendurch nicht geschützt, reagiert aber empfindlich auf Änderungen,

ein synchronized-Block drumherum würde helfen, auch wenn deine bisherigen Versuche vielleicht Fehler enthielten,
wenn das eine zu lange Blockierung anderer Threads ist, kann man auch über eine Kopie der Liste nachdenken,
auch in einem synchronized-Block
 

darkeye2

Bekanntes Mitglied
ok, danke für die Erklärung, wegen synchronizedList(), hab jetzt wieder eine ArrayList genomen, und synchronized blöcke und bis jetzt schein es zu gehen, mal schauen, ob es auch so bleibt :-D
 

darkeye2

Bekanntes Mitglied
So, jezt mal ne andere Frage, aber passt irgendwie doch hier noch rein, ist immerhin auch das gleiche Programm:

Ich hab jetzt schon mehrere möglichkeiten durchprobiert es auf dem bildschirm "schneien" zu lassen, meine alte methode funktioniert prima, ist aber cpu-lastig (11-13% bei 4 kernen und 3.2GHz)

Die neue Idee war ein BufferedImage, nun hab ich aber sowohl das problem, dass das löschen der flocken, die aus dem bildschirm draußen sind nicht geht, und das es wieder so viel leistung braucht, hier der code (nur für aktuelle möglichkeit, und kann so gleich getestet werden)

Flocke.java
Java:
import java.awt.Dimension;
import java.awt.Point;

public class Flocke {
	private Point pos;
	private Dimension size;
	private int speed;
	
	Flocke(Point position, Dimension dim, int sp){
		pos = position;
		size = dim;
		speed = sp;
	}
	
	public Point getPos(){
		return pos;
	}
	
	public Dimension getSize(){
		return size;
	}
	
	public int getSpeed(){
		return speed;
	}
	
	public void move(){
		pos = new Point(pos.x, pos.y+speed);
	}

}

ImageSnow.java (sorry für die namenswahl, aber nach dem ich schon mindestens 10 weitere klassen habe, die vom ergebniss her alles das gleiche tun ist mir kein name mehr eingefallen^^)
Java:
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JWindow;

import com.sun.awt.AWTUtilities;

public class ImageSnow extends JWindow{
	private static final long serialVersionUID = 1L;
	
	private JWindow fr;
	private BufferedImage img; //das bild mit transparenten hintergrund, auf dem die flocken gezeichnet werden
	private BufferedImage snow; //bild für die schneeflocken
	
	//eigenschaften der Flocken
	private volatile ArrayList<Flocke> flocken = new ArrayList<Flocke>();
	
	//Bildschirmeigenschaften (für größen)
	private int x = getToolkit().getScreenSize().width;
	private int y = getToolkit().getScreenSize().height;
	
	//schneefall-steuerung (mit anfangswerten)
	private volatile int speedF = 10;
	private volatile int strength = 30;
	//private volatile long changeTime = 2000;
	private volatile int fps = 24;
	
	ImageSnow(){
		fr = this;
		init();
		AWTUtilities.setWindowOpaque(fr, false);
		this.getContentPane().setLayout(null);;
		this.setPreferredSize (getToolkit().getScreenSize());
		this.setLocation (new Point (0,0));
		this.setAlwaysOnTop (true);
		this.pack();
		this.setVisible(true);
		
		
		//Erzeugen von neuen Schneeflocken und speichern der werte in den Listen
		new Thread(){
			public void run(){
				while(true){
					//wahrscheinlichkeit, dass eine schneeflocke erzeugt wird
					if(((int)(Math.random() * 100) < strength) && flocken.size() <47){
						//festlegen der werte für neue flocke:
						int size = (int) (Math.random()*25+10);
						Point pos = new Point((int) (Math.random()*x),-50);
						int speed =(int)((((double)size)/100)*speedF);

						//Werte für neue flocke in array speichern
						synchronized(flocken){
							flocken.add(new Flocke(pos, new Dimension(size,size), speed));
						}
					}
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		//Neuzeichnen des Bildes, um die Schneeflocken zu bewegen
		new Thread(){
			public void run(){
				Graphics2D g2d = (Graphics2D) img.getGraphics();
				while(true){
					//Graphics holen
					g2d.setComposite(AlphaComposite.Src);
					g2d.setColor(new Color(0,0,0,0));
					g2d.fillRect(0, 0, x, y);
					int t = flocken.size();
					ArrayList<Integer> arr = new ArrayList<Integer>();
					

					
					//Zeichnen der Flocken
					synchronized(flocken){
						for(int k = 0; k<t;k++){
							Flocke fl = flocken.get(k);
							//System.out.println(fl.getPos().y);
							if(fl.getPos().y<y){
								g2d.drawImage(snow, fl.getPos().x, fl.getPos().y, fl.getSize().width, fl.getSize().height, fr);
								fl.move();
							}else{
								arr.add(k);
							}
						}

						for(int n=0; n<arr.size();n++){
							//System.out.println("Flocke: "+arr.get(n)+" wurde gelöscht");
							flocken.remove(arr.get(n));
						}
						arr.clear();
					}
					
					//Bild auf Bildschirm malen
					fr.repaint();
					
					try {
						Thread.sleep(fps);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		
	}
	
	public void paint(Graphics g){
		super.paint(g);
		//System.out.println("TEst");
		g.drawImage(img,0,0,this);
	}
	
	private void init(){
		//transparentes  Bild erstellen
		img = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB_PRE);
		//schneeflocke laden
		URL url1 = getClass().getResource("schneeflocke.png");
		ImageIcon ico = new ImageIcon(url1);
		snow = new BufferedImage(ico.getIconWidth(), ico.getIconHeight(), BufferedImage.TYPE_INT_ARGB); 
		snow.getGraphics().drawImage(ico.getImage(), 0,0,this);
	}
	
	

	public static void main(String[] args) {
		new ImageSnow();

	}

}

und das entsprechende Bild, dass ich verwende:
http://visually-solved.de/schneeflocke.png
 
S

SlaterB

Gast
das Löschen geht nicht, weil du Integer-Objekte zu removen versuchst, nicht den entsprechenden Index,

schönes Programm ansonsten, ich persönlich bin da auch halb Laie, da brauchst du einen richtigen Experten für Echtzeitgrafik,
ob Swing da geeignet ist?

unten meine Programmversion, erstmal auf normales JFrame umgestellt,
interessante Änderungen:
bisher war das painten des img nicht mit dem Zeichnen abgestimmt, es konnte glaube ich der paint das img gemalt werden wenn da gerade nur die Hälfte der Flocken drauf sind,
das vielleicht ein Grund für Flackern bei mir, du berichtest davon aber nix, insofern vielleicht auch nicht so wichtig,

helfen könnte dagegen vielleicht ein einfaches synchronized auf Flocken, ich habe bei mir eingebaut nach jedem Neubeflocken gewartet wird, bis gezeichnet wurde,
Flackern gibts dann nicht mehr, dennoch ist das Bild leicht unruhig, was einfach daran liegt, dass das painten nur ab und zu geschieht,
mal mit 30 ms Abstand, mal mit 90ms, dagegen habe ich noch kein Mittel,

etwas verschönern läßt sich die Lage aber dadurch, dass die Flocken-Position nicht um einen festen Wert erhöht, sondern abhängig von der Zeit neu berechnet wird, so ist es langfristig eine konstante Bewegung, nur mit Pausen, aber danach etwas größeren Sprüngen zum Ausgleich,
meiner Ansicht nach schöner,

so dass waren paar mir bekannte Tricks,
und siehe auch init()-Methode für eine Vereinfachung
Java:
public class Test {

	public static void main(String[] args) throws Exception {
		new ImageSnow();
	}

}

class ImageSnow extends JFrame {
	private static final long serialVersionUID = 1L;

	private BufferedImage img; // das bild mit transparenten hintergrund, auf
	// dem die flocken gezeichnet werden
	private Image snow; // bild für die schneeflocken

	// eigenschaften der Flocken
	private volatile ArrayList<Flocke> flocken = new ArrayList<Flocke>();

	// Bildschirmeigenschaften (für größen)
	private int x = 1000;
	private int y = 500;

	private long time;
	boolean paint = false;

	// schneefall-steuerung (mit anfangswerten)
	private volatile int speedF = 10;
	private volatile int strength = 40;
	// private volatile long changeTime = 2000;
	private volatile int fps = 20;
	JPanel p = new JPanel() {

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			// System.out.println("TEst");
			g.drawImage(img, 0, 0, this);
			paint = false;

			long newtime = System.currentTimeMillis();
			 System.out.println(newtime -time);
			time = newtime;
			
			synchronized (flocken) {
				flocken.notify();
			}
		}
	};

	ImageSnow() {
		init();
		// AWTUtilities.setWindowOpaque(fr, false);
		add(p);
		// p.setDoubleBuffered(true);

		setLocation(new Point(0, 0));
		// setAlwaysOnTop(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(x, y);
		setVisible(true);

		// Erzeugen von neuen Schneeflocken und speichern der werte in den
		// Listen
		new Thread() {
			public void run() {
				int c = 0;
				while (true) {
					c++;
					// if (c % 10 == 0) {
					// System.out.println(flocken.size());
					// }
					// wahrscheinlichkeit, dass eine schneeflocke erzeugt wird
					if (flocken.size() == 0
							|| (((int) (Math.random() * 100) < strength) && flocken
									.size() < 47)) {
						// festlegen der werte für neue flocke:
						int size = (int) (Math.random() * 25 + 10);
						int nx = (int) (Math.random() * x);
						int speed = (int) ((((double) size) / 100) * speedF);

						// Werte für neue flocke in array speichern
						synchronized (flocken) {
							flocken.add(new Flocke(nx,
									new Dimension(size, size), speed));
						}
					}
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();

		// Neuzeichnen des Bildes, um die Schneeflocken zu bewegen
		new Thread() {
			public void run() {
				Graphics2D g2d = (Graphics2D) img.getGraphics();
				int c = 0;
				ArrayList<Integer> arr = new ArrayList<Integer>();
					while (true) {
						
						c++;
						// Graphics holen
						// g2d.setComposite(AlphaComposite.Src);
						// g2d.setColor(new Color(0, 0, 0, 0));
						g2d.fillRect(0, 0, x, y);
						int t = flocken.size();

						// Zeichnen der Flocken
						synchronized (flocken) {
							for (int k = 0; k < t; k++) {
								Flocke fl = flocken.get(k);
								if (fl.y < y) {
									g2d
											.drawImage(snow, fl.x, fl.y, fl
													.getSize().width, fl
													.getSize().height,
													ImageSnow.this);
									fl.move();
								} else {
									arr.add(k);
								}
							}
							// if (c % 30 == 0) {
							// System.out.println(maxy + ", " + y + ", "
							// + arr.size() + ", " + flocken.size());
							// }

							for (int n = 0; n < arr.size(); n++) {
								// System.out.println("Flocke: "+arr.get(n)+" wurde gelöscht");
								flocken.remove((int) arr.get(n));
							}
							arr.clear();

							p.repaint();
							try {
								flocken.wait();
							} catch (Exception e) {
							}
						}

						// // Bild auf Bildschirm malen
						while (paint) {
						  Thread.sleep(fps);
						}

					}
			}
		}.start();

	}

	private void init() {
		// transparentes Bild erstellen
		img = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB_PRE);
		// schneeflocke laden
		// URL url1 = getClass().getResource();
		ImageIcon ico = new ImageIcon("schneeflocke.png");
		snow = ico.getImage();

		// snow = new BufferedImage(ico.getIconWidth(), ico.getIconHeight(),
		// BufferedImage.TYPE_INT_ARGB);
		// snow.getGraphics().drawImage(ico.getImage(), 0, 0, this);
	}

	public static void main(String[] args) {
		new ImageSnow();

	}

	public static int starty = -50;

	class Flocke {

		public int x;
		public int y = starty;
		private Dimension size;
		private int speed;
		private long start = System.currentTimeMillis();

		Flocke(int xx, Dimension dim, int sp) {
			x = xx;
			size = dim;
			speed = sp;
		}

		public Dimension getSize() {
			return size;
		}

		public int getSpeed() {
			return speed;
		}

		public void move() {
			y = starty + (int) ((time - start) * speed / 20);
		}

	}

}
 
Zuletzt bearbeitet von einem Moderator:

darkeye2

Bekanntes Mitglied
Hi,
das Löschen geht nicht, weil du Integer-Objekte zu removen versuchst, nicht den entsprechenden Index,
Danke, ist ein echt penlicher fehler ... aber dank dir funktionierts jetzt.

Wegen dem Flackern, das hatte ich noch gar nicht, bei mir wird alle 24ms (hab den wert auch auf 40 gestellt gehabt, ging auch) ein neues Bild gezeichnet und es läuft zimlich flüssig, auch die ausgabe erfolgt erst nach dem zeichnen aller Flocken (da repaint erst nach zeichnen des neuen Bildes).

und siehe auch init()-Methode für eine Vereinfachung
Hab ich jetzt bei mir auch so gemacht, ist definitief eine vereinafchung.

Nun funktioniert es zwar soweit prima, aber die CPU Auslastung ist immer noch bei 11-13% was für so ein "einfaches" Programm zu viel ist, hoffe jemand hat da eine Idee. Nutze Swing, da ich eigentlich der Ansicht bin/ war, dass es für sowas noch ausreichen müsste, und weil mir keine Sinnvollen alternatieven einfallen.
 

Marco13

Top Contributor
Die Flocken-Bilder verkleinert zu zeichnen KANN (!) aufwändig sein. Es KÖNNTE (!) schneller sein, sich einen Satz von Sprites zu erstellen, die die Flocken-Bilder in den Größen enthalten, in denen sie auch vorkommen (ca. 10-35) und dann immer gleich die in der passenden Größe zu zeichnen. Dann kann man sich auch erlauben, die einmal mit Antialiasing und den bestmöglichen RenderingHints runterzuskalieren, das dürfte dann etwas hübscher aussehen. Im Moment sehen die Flocken ja ein bißchen aus wie ... Schnee-Griesel :D
 

Ähnliche Java Themen

Neue Themen


Oben