ArrayList的并发线程和后续问题

时间:2016-08-02 09:59:16

标签: java multithreading arraylist

我目前正在编写一个模拟城域网的程序。我已经到了模拟工作的程序阶段,可以模拟车站,火车,轨道,路线和平台。但是,尽管工作了一段时间,但在某些时候,我遇到了一个与ArrayList有关的问题。

每列火车都有一个行程,这是一个轨道的ArrayList,它必须遍历它的旅程。每次火车到达车站时,第一个元素被移除。当火车到达旅程结束时,它会找到相反的路线。

以下是我更新路线的方法。

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.
    }
}

奇怪的是,在java开始抛出IndexOutOfBounds异常之前,这种方法可以运行一段时间。该方法是我的Train类的一部分,并且只由它自己的run方法调用,而不是任何其他类。我有synchronized方法,但这没有帮助。

有什么想法吗?

完整Train班级:

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 + "]";
    }

堆栈跟踪对于整个事物来说太长了,所以我以火车KEN078为例。

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 []

1 个答案:

答案 0 :(得分:0)

所以我仔细看了一下updateItinerary。问题来自行itinerary =(ArrayList)route.getEdgeList();,因为它是对我的数据结构的引用,而不是副本。因此,它无意中删除了曲目!