• Es freut uns dass du in unser Minecraft Forum gefunden hast. Hier kannst du mit über 130.000 Minecraft Fans über Minecraft diskutieren, Fragen stellen und anderen helfen. In diesem Minecraft Forum kannst du auch nach Teammitgliedern, Administratoren, Moderatoren , Supporter oder Sponsoren suchen. Gerne kannst du im Offtopic Bereich unseres Minecraft Forums auch über nicht Minecraft spezifische Themen reden. Wir hoffen dir gefällt es in unserem Minecraft Forum!

ConcurrentModificationException??

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
Hallo Coder,

Ich habe eine Exception gefunden! Yay! Das an sich ist ja nicht schwer.
Aber was genaus soll das sein?

Code:
[13:40:30 WARN]: [ScorpionPvP] Task #7 for ScorpionPvP v1.0 generated an exception
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819) ~[?:1.7.0_03]
at java.util.ArrayList$Itr.next(ArrayList.java:791) ~[?:1.7.0_03]
at pluesch.AntiLogout.TaktImpuls(AntiLogout.java:52) ~[?:?]
at pluesch.AntiLogout$1.run(AntiLogout.java:37) ~[?:?]
at org.bukkit.craftbukkit.v1_7_R2.scheduler.CraftTask.run(CraftTask.java:58) ~[spigot.jar:git-Spigot-1360]
at org.bukkit.craftbukkit.v1_7_R2.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:345) [spigot.jar:git-Spigot-1360]
at net.minecraft.server.v1_7_R2.MinecraftServer.v(MinecraftServer.java:618) [spigot.jar:git-Spigot-1360]
at net.minecraft.server.v1_7_R2.DedicatedServer.v(DedicatedServer.java:273) [spigot.jar:git-Spigot-1360]
at net.minecraft.server.v1_7_R2.MinecraftServer.u(MinecraftServer.java:566) [spigot.jar:git-Spigot-1360]
at net.minecraft.server.v1_7_R2.MinecraftServer.run(MinecraftServer.java:472) [spigot.jar:git-Spigot-1360]
at net.minecraft.server.v1_7_R2.ThreadServerApplication.run(SourceFile:618) [spigot.jar:git-Spigot-1360]

Hier der Code der Klasse:

Code:
public class AntiLogout implements Listener{

    private ArrayList<Player>spieler;
    private ArrayList<Integer>timeout;

    private MainFixes main;
    private String prefix;

    private Farben farben;

    public AntiLogout(MainFixes m)
    {
        m.getServer().getScheduler().scheduleSyncRepeatingTask(m, new Runnable() {

            public void run() {
            TaktImpuls();
            }
            }, 60L, 20L);
        spieler = new ArrayList<Player>();
        timeout = new ArrayList<Integer>();
        main=m;
        prefix= main.getPrefix();
        farben= main.getFarben();
    }

    protected void TaktImpuls()
    {
        if(!timeout.isEmpty())
        {
            for(Integer i:timeout)//Hier tritt die Excepion auf, da waehrend der Iteration evtl. Eintraege entfernt werden
            {
                int index=timeout.indexOf(i);
                if(i<=0)
                {
                    if(spieler.size()>index)
                    {
                        Player p=spieler.get(index);
                        removePvP(p);//Hier wird ein Eintrag entfernt
                    }
                    else
                    {
                        timeout.remove(i);
                    }
                }
                else
                {
                    timeout.set(index, i-1);
                }
            }
        }
    }

    public void addPvP(Player p)
    {
        if(spieler.contains(p))
        {
            int index= spieler.indexOf(p);
            timeout.remove(index);
            spieler.remove(index);
            BarAPI.removeBar(p);
        }
        else
        {
            if(!p.getWorld().getName().equals("KitPvP"))
            {
                if(main.istHauptwelt(p.getWorld().getName()))
                {
                    farben.rotFaerben(p);
                    p.sendMessage(prefix+"§cDu kannst den Spawn nicht mehr betreten!");
                }
                p.sendMessage(prefix+"§cFalls du ausloggst wirst du getötet!");
                p.sendMessage(prefix+"§4*Du bist im PvP-Modus*");
            }
        }
        spieler.add(p);
        timeout.add(8);
        if(p.getWorld().getName().equals("KitPvP"))
        {
            BarAPI.setMessage(p, "§4§l*PvP*", 8);
            return;
        }
        BarAPI.setMessage(p, "§4§l*PvP*", 8);
    }

    public void removePvP(Player p)
    {
        if(spieler.contains(p))
        {
            int index= spieler.indexOf(p);
            spieler.remove(index);
            timeout.remove(index);//Entfernung eines Objekts
            p.sendMessage(prefix+"§2*Du bist nicht mehr im PvP-Modus*");
            BarAPI.removeBar(p);
            farben.entfaerben(p);
        }
    }

    //Events
    @EventHandler
    public void onPlayerLeave(PlayerQuitEvent e)
    {
        Player p=e.getPlayer();
        if(spieler.contains(p))
        {
            if(main.check(p.getLocation()))
            {
                p.setHealth(0.0);
            }
            removePvP(p);
        }
    }

    @SuppressWarnings("deprecation")
    @EventHandler
    public void PvPInteraktion(EntityDamageByEntityEvent e)
    {
        if(e.getEntity() instanceof Player)
        {
            Player angreifer;
            Player opfer= (Player) e.getEntity();
            Entity damager= e.getDamager();
            if(damager instanceof Projectile)
            {
                damager=((Projectile) damager).getShooter();
            }
            if(damager instanceof Player)
            {
                angreifer = (Player) damager;
            }
            else
            {
                return;
            }
            if(angreifer.equals(opfer))
            {
                return;
            }
            if(main.istHauptwelt(opfer.getWorld().getName()))
            {
                if(main.check(angreifer.getLocation())&&main.check(opfer.getLocation())&&!main.hatSchutz(opfer)&&!main.hatSchutz(angreifer))
                {
                    addPvP(angreifer);
                    addPvP(opfer);
                }
            }
            else
            {
                if(main.check(angreifer.getLocation())&&main.check(opfer.getLocation()))
                {
                    addPvP(angreifer);
                    addPvP(opfer);
                }
            }
    
        }
    }

    @EventHandler
    public void onDeath(PlayerDeathEvent e)
    {
        Player p= e.getEntity();
        if(spieler.contains(p))
        {
            this.removePvP(p);
        }
    }
}
  //Wenn ihr Teile oder den ganzen Code haben wollt: bedient euch ;)

Ich habe nach einiger Recherche das Problem gelöst und wollte meine Lösung hier posten, damit niemand anderes noch an dieser Exception verzweifelt.


1. Wann tritt sie auf

Eine ConcurrentModificationException tritt auf, wenn von zwei verschiedenen Threads auf eine Liste zugegriffen wird, oder wenn während der Iteration (Durchgehen aller Objekte einer Liste), ein Objekt entfernt werden könnte.


2. Wie löst man es "richtig"

Falls es sich hierbei um einen Multi-Thread Umgebung handelt, könnt diese Klasse helfen:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentMap.html


Bei einer Single-Thread Umgebung empfehlt sich eine Lösung mit lokaler Variable, die mit der "for"-Schleife Iteriert wird (oder mit Iteratoren, wie man das halt lieber macht):
Code:
for(Integer i : new ArrayList<Integer>(timeout))//by MiCrJonas1997

Hier noch ein Link der von @manf gepostet wurde, bezüglich weiterer Möglichkeiten:
http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html

Ich hoffe ich konnte dem ein oder anderen helfen ;)

MfG
_Baum_
 
Zuletzt bearbeitet:

MiCrJonas

Threadripper
Registriert
29 Oktober 2012
Beiträge
1.064
Diamanten
0
Code:
for(Integer i : new ArrayList<Integer>(timeout))

Du gehst die Liste an einer Stelle durch und löschst dabei eventuell Einträge. Das geht nicht. Probier mal meinen Code aus. Da wird nicht die gleiche Liste durchlaufen, in der auch die Elemente gelöscht werden.
 

xMyPhatex

Minecrafter
Registriert
25 August 2014
Beiträge
7
Diamanten
0
Minecraft
xMyPhatex
@MiCrJonas1997:
Er hat die Lösung bereits gefunden und wollte diese mit uns teilen. =)

@_Baum_:
Ähnliche Probleme habe ich meist mit laufenden Liste wie OnlinePlayers oder OfflinePlayers. Daher packe, sollte ich zwingend eine Liste verwenden müssen, diese nochmal in eine lokale Variable. Gerade dann wenn die Ausführungen in den Schleifen größer sind.
 

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
Code:
for(Integer i : new ArrayList<Integer>(timeout))

Du gehst die Liste an einer Stelle durch und löschst dabei eventuell Einträge. Das geht nicht. Probier mal meinen Code aus. Da wird nicht die gleiche Liste durchlaufen, in der auch die Elemente gelöscht werden.

Ach so einfach wärs gewesen xD

Naja wenigstens kann ich jetzt Maps benutzen ;)
 

MiCrJonas

Threadripper
Registriert
29 Oktober 2012
Beiträge
1.064
Diamanten
0
Das ganze hat absolut nichts mit Threads/Threadsicherheit zu tun. Dein ganzer Code wird im selben Thread ausgeführt. Das Problem ist, dass du mit einem Iterator (benutzt von deiner erweiterten for-Schleife) die Einträge der Liste durchgehst und während du die Liste durchgehst, versuchst du schon Einträge zu löschen. Wie gesagt, das geht nicht. Bei dem Beispiel von mir wird eine Kopie der Liste durchgegangen, weshalb du die originale Liste bearbeiten kannst bzw. Einträge löschen kannst.
 

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
*EDIT* Habe keine Ahnung hiervon, weiß nicht, ob das stimmt

@MiCrJonas1997

Nur das es eine Klasse ist, heißt es nicht, dass es alles im gleichen Thread abläuft!
http://wiki.bukkit.org/Scheduler_Programming/de

Der TaskScheduler macht GENAU das: er registriert neue Threads, die bestimmte Aufgaben ausführen!

Auch Swing Timer laufen unabhängig vom Main Thread in einem asynchronen Thread:
http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Zitat daraus:
Note that the Swing timer's task is performed in the event dispatch thread.

Und auch Bukkit-Events laufen in einem separaten Thread: die synchronen in einem synchronen und die asynchronen in einem asynchronen.

Korrigiere mich, wenn ich mich täusche

MfG
_Baum_
 
Zuletzt bearbeitet:

MiCrJonas

Threadripper
Registriert
29 Oktober 2012
Beiträge
1.064
Diamanten
0
Zur gleichen Zeit wird (Ja, Threads laufen [in Bukkit] nie gleichzeitig) nicht aus 2 Threads auf die Variablen zugegriffen, weil alles von
"TaktImpuls()" (Methoden klein *hust*) ausgeht, und danach kein neuer Thread gestartet wird. Die anderen Zugriffe geschehen viel früher.
 
Zuletzt bearbeitet:

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
*EDIT* Gequirlte Scheiße: einfach nicht lesen

Threads laufen in Bukkit nie gleichzeitig? Die Aufteilung in Haupt- und Nebenthreads basiert ja auf dem Fakt, dass der Main-Thread nicht angehalten werden darf, also durchgehend läuft. Heißt das nicht automatisch, dass ab und zu mehrere Thread gleichzeitig laufen müssen?

@manf
Nun ja, nehmen wir mal an, dass der Event Threat und der Synchrone Thread des Tasks gleichzeitig laufen.
Dann wäre der Grund:
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future. Aus http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html

Und die Lösung (aus http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html):
To Avoid ConcurrentModificationException in multi-threaded environment:

1. You can convert the list to an array and then iterate on the array. This approach works well for small or medium size list but if the list is large then it will affect the performance a lot.

2. You can lock the list while iterating by putting it in a synchronized block. This approach is not recommended because it will cease the benefits of multithreading.

3. If you are using JDK1.5 or higher then you can use ConcurrentHashMap and CopyOnWriteArrayList classes. It is the recommended approach. Die Lösung, auf die ich kam.

Meine Annahme, dass mein Plugin mehrere Threads nutz beruht auf diesem Artikel: http://wiki.bukkit.org/Scheduler_Programming/de

Thread 1: Haupthread
Thread 2: Event- Thread
Thread 3: SyncRepeatingTask - Thread

MfG
_Baum_
 
Zuletzt bearbeitet:

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
Na gut ^^
Haupsache das Problem ist gelöst, danke für eure Mithilfe :D
Ich arbeite erst seit einem Jahr mit Java, davon der Großteil in der Schule, da habe ich noch nicht das perfekte Fingerspitzengefühl, was solche Sachen betrifft ;)
 

_Baum_

Kuhfänger
Registriert
4 August 2013
Beiträge
66
Diamanten
0
Minecraft
PlueschAffe
*EDIT*
Halbwissen ist schlimmer als Unwissen -.-

Code:
[14:52:47] [Server thread/WARN]: [ScorpionPvP] Task #7 for ScorpionPvP v1.0 generated an exception
java.util.ConcurrentModificationException

Daraufhin hatte ich das dringende Bedürfnis in meine Tastatur zu beißen...

Eigener Thread zu diesem Thema folgt...

@MiCrJonas1997 nehme jetzt deine Lösung, nochmal danke dafür :)
Code:
public class AntiLogout implements Listener{
 
    private HashMap<Player,Integer> liste;
 
    private MainFixes main;
    private String prefix;
 
    private Farben farben;
 
    public AntiLogout(MainFixes m)
    {
        liste= new HashMap<Player,Integer>();
        m.getServer().getScheduler().scheduleSyncRepeatingTask(m, new Runnable() {

            public void run() {
            taktImpuls();
            }
            }, 60L, 20L);
        main=m;
        prefix= main.getPrefix();
        farben= main.getFarben();
    }
 
    protected void taktImpuls() //Methode zu Feier des Tages klein ^^
    {
        HashMap<Player,Integer> liste1= new HashMap<Player,Integer>(this.liste);
        if(!liste.isEmpty())
        {
            for(Iterator<Player> it=liste1.keySet().iterator();it.hasNext();)
            {
                Player p= it.next();
                int i= liste1.get(p);
                if(i<=0)
                {
                    liste1=removePvP(p,liste1);
                    this.liste=liste1;
                    return;
                }
                else
                { 
                    liste1.put(p, i-1);
                }
            }
            this.liste=liste1;
        }
    }

    public void addPvP(Player p)
    {
        HashMap<Player,Integer> liste1= new HashMap<Player,Integer>(this.liste);//Auf Nummer sicher gehen
        if(liste1.containsKey(p))
        {
            liste1.put(p, 8);
            BarAPI.removeBar(p);
        }
        else
        {
           liste1.put(p, 8);
           BarAPI.setMessage(p, "§4§l*PvP*", 8);
        }
    this.liste=liste1;
    }
 
    public HashMap<Player,Integer> removePvP(Player p,HashMap<Player,Integer> liste1)
    {
        if(liste1.containsKey(p))
        {
            liste1.remove(p);
            p.sendMessage(prefix+"§2*Du bist nicht mehr im PvP-Modus*");
            BarAPI.removeBar(p);
            if(main.hatSchutz(p))
            {
                farben.gruenFaerben(p);
            }
            else
            {
                farben.entfaerben(p);
            }
        }
        return liste1;
    }
 
    //Events
    @EventHandler
    public void onPlayerLeave(PlayerQuitEvent e)
    {
        Player p=e.getPlayer();
        if(liste.containsKey(p))
        {
            if(main.check(p.getLocation()))
            {
                p.setHealth(0.0);
            }
            liste=removePvP(p,liste);
        }
    }
 
    @SuppressWarnings("deprecation")
    @EventHandler
    public void interaktionPvP(EntityDamageByEntityEvent e)
    {
        if(e.getEntity() instanceof Player)
        {
            Player angreifer;
            Player opfer= (Player) e.getEntity();
            Entity damager= e.getDamager();
            if(damager instanceof Projectile)
            {
                damager=((Projectile) damager).getShooter();
            }
            if(damager instanceof Player)
            {
                angreifer = (Player) damager;
            }
            else
            {
                return;
            }
            if(angreifer.equals(opfer))
            {
                return;
            }
            if(main.istHauptwelt(opfer.getWorld().getName()))
            {
                if(main.check(angreifer.getLocation())&&main.check(opfer.getLocation())&&!main.hatSchutz(opfer)&&!main.hatSchutz(angreifer))
                {
                    addPvP(angreifer);
                    addPvP(opfer);
                }
            }
            else
            {
                if(main.check(angreifer.getLocation())&&main.check(opfer.getLocation()))
                {
                    addPvP(angreifer);
                    addPvP(opfer);
                }
            }
        }
    }


    @EventHandler
    public void onDeath(PlayerDeathEvent e)
    {
        Player p= e.getEntity();
        if(liste.containsKey(p))
        {
            liste=removePvP(p,liste);
        }
    }
}

Hiezu noch eine letzte Frage: Kann das "inteaktionPvP" Event ausgeführt werden, währed die Iteration im "taktImpuls" abläuft?
Dann müsste ich den Code ein wenig umstellen...

Das wär dann auch die letzte Sache in diesem Thread (obwohl sie nicht zum Rest passt), danke im voraus!

MfG

_Baum_
 
Oben