2016-08-02 13 views
1

Ich schreibe gerade ein Programm, das ein Metro-Netzwerk simuliert. Ich habe eine Stufe in dem Programm erreicht, wo die Simulation funktioniert, und kann Stationen, Züge, Tracks, Routen und Plattformen simulieren. Obwohl ich irgendwann arbeite, bekomme ich irgendwann ein Problem mit einer ArrayList.Parallele Threads und Folgeprobleme mit ArrayList

Jeder Zug hat eine Reiseroute, die eine ArrayListe von Gleisen ist, die er auf seiner Reise durchqueren muss. Jedes Mal, wenn der Zug eine Station erreicht, wird das erste Element entfernt. Wenn der Zug das Ende dieser Reise erreicht, findet er die entgegengesetzte Route.

Unten ist meine Methode, um die Route zu aktualisieren.

private synchronized void updateItinerary() { 
    itinerary.remove(0); // Remove the first element. 
    if (itinerary.isEmpty()) { 
     String id = this.id.substring(0,3) + "-" + Track.generateTrackId(route.getEndVertex().id, route.getStartVertex().id); // Get the route identification. 
     route = metro.routes.get(id); // Retrieve the new route, and switch. 
     itinerary = (ArrayList<Track>) route.getEdgeList(); // Update the itinerary. 
    } 
} 

Das Merkwürdige ist, dass diese Methode für einen bestimmten Zeitraum funktioniert, bevor Java IndexOutOfBounds Ausnahmen beginnt zu werfen. Die Methode ist Teil meiner Klasse Train und wird nur durch ihre eigene Methode run und niemals durch eine andere Klasse aufgerufen. Ich habe synchronized die Methode, aber das hat nicht geholfen.

Irgendwelche Ideen?

Voll Train Klasse:

package metro; 

import java.util.ArrayList; 

import org.jgrapht.graph.GraphPathImpl; 

/** 
* Represents a train on the network. 
* @author J.D. Preece 
*/ 
public class Train implements Runnable { 

    protected static int trainId = 0; // A unique serial number for each train created. 

    protected ArrayList<Track> itinerary; // The tracks that this train is to cover on this journey. 
    protected boolean inTransit; // Indicates whether the train is in transit or not. 
    protected GraphPathImpl<Station, Track> route; // The route this train is on. 
    protected int delay; // The delay of this train. 
    protected Metro metro; // The metro network this train is on. 
    protected Platform platform; // The platform this train is currently on. 
    protected Station station; // The station this train is currently at. 
    protected String id; // The identification token of this train. 
    protected Track track; // The track this train is currently on. 

    /** 
    * Creates a new train. 
    * @param id The identification token of this train. 
    * @param metro The metro network this train is on. 
    * @param track The track this train is starting on. 
    * @param route The route this train is starting on. 
    */ 
    public Train(String id, Metro metro, Track track, GraphPathImpl<Station, Track> route) { 
     this.inTransit = true; // Declares the train is in transit. 
     this.delay = 0; // Sets the initial delay to 0. 
     this.metro = metro; 
     this.track = track; 
     this.route = route; 
     this.station = null; 
     this.id = id; 
     this.itinerary = generateInitialItinerary(); // Generate the initial itinerary for this train. 
    } 

    /** 
    * Generates an identification token for a train. 
    * @param routeId The identification token of the route the train is on. 
    * @return An identification token for a train. The token will be the three letters of the line it is on, followed by a unique number. 
    */ 
    public static String generateTrainId(String routeId) { 
     trainId++; 
     return routeId.substring(0,3) + String.format("%03d", trainId); 
    } 

    /** 
    * Simulates a train running. 
    */ 
    @Override 
    public void run() { 
     try { 
      while (true) { 
       if (inTransit) { 
        traverseTrack(); // Travel across the current section of track, until it reaches the end. 
        joinStationQueue(track.target); // Join the queue for the next station to wait for an available platform. 
        arriveAtStation(track.target); // Update the current station of this train, and free the track it has just left. 
       } else { 
        joinTrackQueue(itinerary.get(0)); // Wait until the next set of track is free. 
        departStation(); // Depart the station, and join the next track. 
       } 
      } 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * Determines what the train does as it traverses across the track it is on. 
    * @throws InterruptedException 
    */ 
    private void traverseTrack() throws InterruptedException { 
     double locationOnTrack = 0; // Indicates where the train is on this section of track. 
     while (locationOnTrack < track.weight) { 
      Thread.sleep(1); // Sleep briefly. 
      locationOnTrack++; // Iterate the location of this train on this section of track. 
     } 
    } 

    /** 
    * Adds this train to the queue for a platform at the next station, and waits to be notified of a free platform. 
    * @param station 
    * @throws InterruptedException 
    */ 
    private void joinStationQueue(Station station) throws InterruptedException { 
     synchronized (station) { 
      station.waitingTrains.add(this); // Add this train to the target station queue. 
     } 
     System.out.println("The next station for " + id + " is " + station.toString()); // Outputs the arrival to the console. 
     synchronized (this) { 
      this.wait(); // Wait until a notification is received. 
     } 
    } 

    /** 
    * Adds this train to the queue to use this section of track, and waits to be notified that it is free. 
    * @throws InterruptedException 
    */ 
    private void joinTrackQueue(Track track) throws InterruptedException { 
     synchronized (track) { 
      track.waitingTrains.add(this); 
     } 
     synchronized (this) { 
      this.wait(); 
     } 
    } 

    /** 
    * Updates the current station of this train, and frees the track it was just on. 
    * @param station The station this train is arriving at. 
    */ 
    private void arriveAtStation(Station station) { 
     inTransit = false; // Declares that the train is no longer in transit. 
     this.station = station; // Updates the current station. 
     synchronized (track) { 
      track.notify(); // Notify the track that it is now free. 
      track = null; // Removes the reference to that section of track. 
     } 
     updateItinerary(); 
     System.out.println(id + " has arrived at " + station); 
     System.out.println(id + " " + itinerary); 
    } 

    /** 
    * Departs the train from the current station, and notifies the station of the departure. 
    */ 
    private void departStation() { 
     synchronized (platform) { 
      platform.notify(); // Notifies the platform of the departure. 
      platform = null; // Removes the current platform reference. 
      station = null; // Removes the current station reference. 
      inTransit = true; // Declares that the train is now in transit. 
     } 
    } 

    /** 
    * Generates the initial itinerary for the train. 
    * @return A list of tracks to be traversed until the destination station. 
    */ 
    private ArrayList<Track> generateInitialItinerary() { 
     ArrayList<Track> newList = new ArrayList<Track>(route.getEdgeList()); 
     for (Track track : route.getEdgeList()) { 
      if (this.track.equals(track)) { 
       return newList; // If the current track being checked is the track the train is actually on, return the itinerary. 
      } else { 
       newList.remove(0); // Remove redundant track. 
      } 
     } 
     return newList; 
    } 

    /** 
    * Updates the itinerary of this train. If the train has reached the end of its previous journey, it is given a new one in the opposite direction. 
    */ 
    private synchronized void updateItinerary() { 
     itinerary.remove(0); 
     if (itinerary.isEmpty()) { 
      String id = this.id.substring(0,3) + "-" + Track.generateTrackId(route.getEndVertex().id, route.getStartVertex().id); 
      route = metro.routes.get(id); // Switch routes. 
      itinerary = (ArrayList<Track>) route.getEdgeList(); // Update the itinerary. 
     } 
    } 

    /** 
    * Outputs this train as a string. 
    * @return Information on this train. 
    */ 
    public String toString() { 
     return "[" + id + "]"; 
    } 

Der Stack-Trace ist zu lang für die ganze Sache, so habe ich das Beispiel Zug KEN078 genommen.

The next station for KEN078 is [12 : Station Hill] 
KEN078 has arrived at [12 : Station Hill] 
KEN078 [[42:47 : Kennet Island : Madjeski Stadium : 900.0], [47:51 : Madjeski Stadium : Reading International Business Park : 950.0], [51:53 : Reading International Business Park : Three Mile Cross : 850.0]] 

--- 

The next station for KEN078 is [47 : Madjeski Stadium] 
Exception in thread "Thread-546" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 
    at java.util.ArrayList.rangeCheck(ArrayList.java:653) 
    at java.util.ArrayList.get(ArrayList.java:429) 
    at metro.Train.run(Train.java:65) 
    at java.lang.Thread.run(Thread.java:745) 
The next station for SUT117 is [38 : Theale] 
KEN078 has arrived at [47 : Madjeski Stadium] 
KEN078 [] 
+0

Bitte geben Sie Ihren Code vollständig ein, um zu antworten – Shriram

+0

Wird "route" strukturell anderswo in der Klasse geändert? Sind diese Orte auch synchronisiert? –

+0

@Shriram, ich habe meine komplette Train-Klasse hochgeladen. – SensibleCape11

Antwort

0

Also habe ich updateItinerary genauer unter die Lupe genommen. Das Problem kam aus der Zeile route = (ArrayList) route.getEdgeList() ;, da es sich um eine Referenz auf meine Datenstruktur handelte, keine Kopie. Daher wurden Spuren unbeabsichtigt entfernt!

Verwandte Themen