简体   繁体   中英

Implementing a for-loop over a LinkedList in a run method in Java: java.util.ConcurrentModificationException

I am looking for a solution to problem I imagine to be rather simple, but cannot figure out for some reason. Since I have just started programming a couple of weeks ago, I am sure that this code might be somewhat less than perfectly written, so please bear with me.

The problem I have with the code, is that it throws an exception at the for-loop "for (Bestellung bestellung: bestellungenInProduktion){" (lower third of code; marked with comment) as soon as I "submit" 3 or more "orders" at the same time, while it runs perfectly fine when only submitting 1 or 2 orders at the same time.

When submitting 3 or more orders, the first order goes through just fine, but it throws an exception as soon as the production of the first order has been completed. The error message is:

Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.base/java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:970)
    at java.base/java.util.LinkedList$ListItr.next(LinkedList.java:892)
    at Produktions_Manager.run(Produktions_Manager.java:88)

This is the code in question (exception-throwing line is ca. 25 lines above the last):

public class Produktions_Manager extends Thread
{
    // Initiates all instant variables. 
    private Holzbearbeitungs_Roboter holzroboter; // robots
    private Montage_Roboter montageroboter;
    private Lackier_Roboter lackierroboter;
    private Verpackungs_Roboter verpackungsroboter;

    private Fabrik meineFabrik; //myFactory
    private Lager meinLager; //myStorage
    
    private LinkedList<Bestellung> zuVerarbeitendeBestellungen; //ordersToBeCompleted
    private LinkedList<Bestellung> bestellungenInProduktion; //ordersInProduction

    /**
     * The constructor of the Produktions_Manager class instantiates and starts
     * all robots. In also opens the LinkedLists for the orders to be processed
     * and for the orders in production.
     * 
     * @param   meineFabrik   die Fabrik, die eröffnet wird
     * @param   meinLager     das Lager, das eröffnet wird
     */
    public Produktions_Manager(Fabrik meineFabrik, Lager meinLager)
    {
        holzroboter = new Holzbearbeitungs_Roboter("Holzroboter");
        montageroboter = new Montage_Roboter("Montageroboter");
        lackierroboter = new Lackier_Roboter("Lackierroboter");
        verpackungsroboter = new Verpackungs_Roboter("Verpackungsroboter");
        
        holzroboter.start();
        montageroboter.start();
        lackierroboter.start();
        verpackungsroboter.start();

        this.meineFabrik = meineFabrik;
        this.meinLager = meinLager;        
        
        zuVerarbeitendeBestellungen = new LinkedList<Bestellung>();
        bestellungenInProduktion = new LinkedList<Bestellung>();
    }
    
    /**
     * This method checks in an infinite loop if a new order has arrived.
     * If an order has been received, it is removed from the list of orders to be processed
     * and added to the list of products to be produced. Subsequently, the production is started.
     */


    @Override public void run()
    {
        ThreadUtil.syncedPrintln("Produktionsmanager wurde gestartet");
        
        while (true){    //When a new order has arrived, then get the next order and start production.

            Bestellung naechsteBestellung = zuVerarbeitendeBestellungen.peek();
            
            if(naechsteBestellung == null){
                ThreadUtil.syncedPrintln("[Produktions_Manager] keine Bestellung zu verarbeiten");
            } else {
                ThreadUtil.syncedPrintln("[Produktions_Manager] neue Bestellung zu verarbeiten");
            }
                        
            if(naechsteBestellung != null && meinLager.lagerbestandPruefenUndNachbestellen(naechsteBestellung)){
                naechsteBestellung = zuVerarbeitendeBestellungen.poll();          
                bestellungenInProduktion.add(naechsteBestellung); 
                naechsteBestellung.setzeBestellstatus(2);
                
                ThreadUtil.syncedPrintln("[Produktions_Manager] startet die Produktion von " + naechsteBestellung.gibBestellungsNr());
                
                for(Produkt produkt : naechsteBestellung.liefereBestellteProdukte()){
                    roboterzuweisung(produkt);
                    produkt.naechsteProduktionsstation();
                }
            }
            
            //The next line is where the exception is thrown.

            for (Bestellung bestellung : bestellungenInProduktion){ //Order order : ordersInProduction
                boolean alleProdukteProduziert = true; //allItemsProduced
                for(Produkt produkt : bestellung.liefereBestellteProdukte()){ //order.deliverOrdererItems
                    if(produkt.gibAktuellenZustand()!=3){ //product.giveCurrentState
                        alleProdukteProduziert = false; //allItemsProduced
                        break;
                    }
                }
                
                if(alleProdukteProduziert == true){
                 bestellungenInProduktion.remove(bestellung);
                 bestellung.setzeAlleProdukteProduziert();
                 
                }
            }

            //Zusätzliche Meldung, falls keine Bestellungen in der Produktions-Pipeline stehen
            if(bestellungenInProduktion.size() == 0){
                ThreadUtil.syncedPrintln("[Produktions_Manager] keine neuen Bestellungen für Produktion - Roboter können nichts produzieren.");
            } else {
                ThreadUtil.syncedPrintln("[Produktions_Manager] es sind noch Bestellungen in Produktion.");
            }
            
            ThreadUtil.sleep(5000);
            
        }
    }
}

Because it works with 1 and 2 orders submitted at the same time, I would expect it to work with more as well, but there seems to be a synchronization issue to which I cannot find the solution.

I tried to use an Iterator (which I tried in different ways for many hours), as well as adding sleeps (in separate class; works everywhere else) of various durations in various places (because that has solved some problems for me before), all of which has not worked.

Unfortunately, to not use LinkedList, would mean to almost start my code from the top again, which I would therefore like to solve with another work-around if possible.

I would be immensely grateful if someone could help me out or point me in the right direction. And if there is any further information required, I will make sure to add it to this post as quickly as possible.

If I got it right you have a for loop over a list ( bestellungInProduktion ), and inside that loop, you call remove on that list. That will cause the ConcurrentModificationException , as expected.

Internally, the enhanced for loop creates an iterator on that list, and that will go out of sync through removing (or adding) an element from the list.

If you want to remove an entry from the list that you currently iterate over, you have to call Iterator::remove :

for( final var i = bestellungenInProduktion.listIterator(); i.hasNext(); )
{ 
  var alleProdukteProduziert = true;
  final var bestellung = i.next;
  for( final var produkt : bestellung.liefereBestellteProdukte() )
  { 
    if( produkt.gibAktuellenZustand() != 3 )
    {
      alleProdukteProduziert = false;
      break;
    }
  }
                
  if( alleProdukteProduziert )
  {
    i.remove();
    bestellung.setzeAlleProdukteProduziert();
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM