Hallo,
ich nutze mehrere Listen, die ich auf folgende weiße erstelle:
Java:
privatevolatileList<Dimension> size =Collections.synchronizedList(newArrayList<Dimension>());privatevolatileList<Point> position =Collections.synchronizedList(newArrayList<Point>());privatevolatileList<Integer> speed =Collections.synchronizedList(newArrayList<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 pososition){...}) 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.
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
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
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)
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:
importjava.awt.AlphaComposite;importjava.awt.Color;importjava.awt.Dimension;importjava.awt.Graphics;importjava.awt.Graphics2D;importjava.awt.Point;importjava.awt.image.BufferedImage;importjava.net.URL;importjava.util.ArrayList;importjavax.swing.ImageIcon;importjavax.swing.JWindow;importcom.sun.awt.AWTUtilities;publicclassImageSnowextendsJWindow{privatestaticfinallong serialVersionUID =1L;privateJWindow fr;privateBufferedImage img;//das bild mit transparenten hintergrund, auf dem die flocken gezeichnet werdenprivateBufferedImage snow;//bild für die schneeflocken//eigenschaften der FlockenprivatevolatileArrayList<Flocke> flocken =newArrayList<Flocke>();//Bildschirmeigenschaften (für größen)privateint x =getToolkit().getScreenSize().width;privateint y =getToolkit().getScreenSize().height;//schneefall-steuerung (mit anfangswerten)privatevolatileint speedF =10;privatevolatileint strength =30;//private volatile long changeTime = 2000;privatevolatileint fps =24;ImageSnow(){
fr =this;init();AWTUtilities.setWindowOpaque(fr,false);this.getContentPane().setLayout(null);;this.setPreferredSize (getToolkit().getScreenSize());this.setLocation (newPoint(0,0));this.setAlwaysOnTop (true);this.pack();this.setVisible(true);//Erzeugen von neuen Schneeflocken und speichern der werte in den ListennewThread(){publicvoidrun(){while(true){//wahrscheinlichkeit, dass eine schneeflocke erzeugt wirdif(((int)(Math.random()*100)< strength)&& flocken.size()<47){//festlegen der werte für neue flocke:int size =(int)(Math.random()*25+10);Point pos =newPoint((int)(Math.random()*x),-50);int speed =(int)((((double)size)/100)*speedF);//Werte für neue flocke in array speichernsynchronized(flocken){
flocken.add(newFlocke(pos,newDimension(size,size), speed));}}try{Thread.sleep(100);}catch(InterruptedException e){
e.printStackTrace();}}}}.start();//Neuzeichnen des Bildes, um die Schneeflocken zu bewegennewThread(){publicvoidrun(){Graphics2D g2d =(Graphics2D) img.getGraphics();while(true){//Graphics holen
g2d.setComposite(AlphaComposite.Src);
g2d.setColor(newColor(0,0,0,0));
g2d.fillRect(0,0, x, y);int t = flocken.size();ArrayList<Integer> arr =newArrayList<Integer>();//Zeichnen der Flockensynchronized(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();}publicvoidpaint(Graphics g){super.paint(g);//System.out.println("TEst");
g.drawImage(img,0,0,this);}privatevoidinit(){//transparentes Bild erstellen
img =newBufferedImage(x, y,BufferedImage.TYPE_INT_ARGB_PRE);//schneeflocke ladenURL url1 =getClass().getResource("schneeflocke.png");ImageIcon ico =newImageIcon(url1);
snow =newBufferedImage(ico.getIconWidth(), ico.getIconHeight(),BufferedImage.TYPE_INT_ARGB);
snow.getGraphics().drawImage(ico.getImage(),0,0,this);}publicstaticvoidmain(String[] args){newImageSnow();}}
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:
publicclassTest{publicstaticvoidmain(String[] args)throwsException{newImageSnow();}}classImageSnowextendsJFrame{privatestaticfinallong serialVersionUID =1L;privateBufferedImage img;// das bild mit transparenten hintergrund, auf// dem die flocken gezeichnet werdenprivateImage snow;// bild für die schneeflocken// eigenschaften der FlockenprivatevolatileArrayList<Flocke> flocken =newArrayList<Flocke>();// Bildschirmeigenschaften (für größen)privateint x =1000;privateint y =500;privatelong time;boolean paint =false;// schneefall-steuerung (mit anfangswerten)privatevolatileint speedF =10;privatevolatileint strength =40;// private volatile long changeTime = 2000;privatevolatileint fps =20;JPanel p =newJPanel(){publicvoidpaintComponent(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(newPoint(0,0));// setAlwaysOnTop(true);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setSize(x, y);setVisible(true);// Erzeugen von neuen Schneeflocken und speichern der werte in den// ListennewThread(){publicvoidrun(){int c =0;while(true){
c++;// if (c % 10 == 0) {// System.out.println(flocken.size());// }// wahrscheinlichkeit, dass eine schneeflocke erzeugt wirdif(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 speichernsynchronized(flocken){
flocken.add(newFlocke(nx,newDimension(size, size), speed));}}try{Thread.sleep(100);}catch(InterruptedException e){
e.printStackTrace();}}}}.start();// Neuzeichnen des Bildes, um die Schneeflocken zu bewegennewThread(){publicvoidrun(){Graphics2D g2d =(Graphics2D) img.getGraphics();int c =0;ArrayList<Integer> arr =newArrayList<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 Flockensynchronized(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 malenwhile(paint){Thread.sleep(fps);}}}}.start();}privatevoidinit(){// transparentes Bild erstellen
img =newBufferedImage(x, y,BufferedImage.TYPE_INT_ARGB_PRE);// schneeflocke laden// URL url1 = getClass().getResource();ImageIcon ico =newImageIcon("schneeflocke.png");
snow = ico.getImage();// snow = new BufferedImage(ico.getIconWidth(), ico.getIconHeight(),// BufferedImage.TYPE_INT_ARGB);// snow.getGraphics().drawImage(ico.getImage(), 0, 0, this);}publicstaticvoidmain(String[] args){newImageSnow();}publicstaticint starty =-50;classFlocke{publicint x;publicint y = starty;privateDimension size;privateint speed;privatelong start =System.currentTimeMillis();Flocke(int xx,Dimension dim,int sp){
x = xx;
size = dim;
speed = sp;}publicDimensiongetSize(){return size;}publicintgetSpeed(){return speed;}publicvoidmove(){
y = starty +(int)((time - start)* speed /20);}}}
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.
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