Site Search:

Java concurrency example -- Load the ark with multithreading part 3 code

<Back


Event though loadedAnimals is a thread safe collection by itself. Accessing its iterator while other threads are modifying it will throw ConcurrentModificationException. In this program we fixed the ConcurrentModificationException by guarding the access to loadedAnimals.

import java.util.*;

public class Animals {
    Ark ark;
    Species species;
    Gender gender;

    public int loadTheArk(Collection<Animal> candidates) {
        SortedSet<Animal> animals;
        int numPairs = 0;
        Animal candidate = null;

        // animals confined to method, don't let them escape!
        animals = new TreeSet<Animal>(new SpeciesGenderComparator());
        animals.addAll(candidates);
        for (Animal a : animals) {
            if (candidate == null || !candidate.isPotentialMate(a))
                candidate = a;
            else {
                ark.load(new AnimalPair(candidate, a));
                ++numPairs;
                candidate = null;
            }
        }
        return numPairs;
    }
    
    public static void main(String...args) {
        Animals animals = new Animals();
        animals.ark = animals.new Ark();
        
        Thread helper = new Thread(new Runnable(){
            @Override
            public void run() {
                Collection<Animal> candidates2 = new TreeSet<Animal>(animals.new SpeciesGenderComparator());
                for(Species s: Species.values()) {
                    for(Gender g: Gender.values()) {
                        if(s.toString().charAt(0) <= 'H') {
                            candidates2.add(animals.new Animal(s, g));
                        }
                    }
                }
                System.out.println(animals.loadTheArk(candidates2) + " pairs of animals loaded");
                System.out.println(Species.UNICORN + " is on the ark? " + animals.ark.hasAminal(Species.UNICORN));
                System.out.println(Species.AARDVARK + " is on the ark? " + animals.ark.hasAminal(Species.AARDVARK));

            }});
        helper.start();
        
        Collection<Animal> candidates = new TreeSet<Animal>(animals.new SpeciesGenderComparator());
        for(Species s: Species.values()) {
            for(Gender g: Gender.values()) {
                if(s.toString().charAt(0) >= 'H') {
                    candidates.add(animals.new Animal(s, g));
                }
            }
        }
        System.out.println(animals.loadTheArk(candidates) + " pairs of animals loaded");
        System.out.println(Species.UNICORN + " is on the ark? " + animals.ark.hasAminal(Species.UNICORN));
        System.out.println(Species.AARDVARK + " is on the ark? " + animals.ark.hasAminal(Species.AARDVARK));
    }

    class Animal {
        Species species;
        Gender gender;
        
        public Animal(Species species, Gender gender) {
            this.species = species;
            this.gender = gender;
        }

        public boolean isPotentialMate(Animal other) {
            return species == other.species && gender != other.gender;
        }
    }

    enum Species {
        AARDVARK, BENGAL_TIGER, CARIBOU, DINGO, ELEPHANT, FROG, GNU, HYENA,
        IGUANA, JAGUAR, KIWI, LEOPARD, MASTADON, NEWT, OCTOPUS,
        PIRANHA, QUETZAL, RHINOCEROS, SALAMANDER, THREE_TOED_SLOTH,
        UNICORN, VIPER, WEREWOLF, XANTHUS_HUMMINBIRD, YAK, ZEBRA
    }

    enum Gender {
        MALE, FEMALE
    }

    class AnimalPair {
        //logical equality?
        private final Animal one, two;

        public AnimalPair(Animal one, Animal two) {
            this.one = one;
            this.two = two;
        }
    }

    class SpeciesGenderComparator implements Comparator<Animal> {
        public int compare(Animal one, Animal two) {
            int speciesCompare = one.species.compareTo(two.species);
            return (speciesCompare != 0)
                    ? speciesCompare
                    : one.gender.compareTo(two.gender);
        }
    }

    class Ark {
        private final Set<AnimalPair> loadedAnimals = new HashSet<AnimalPair>();

        public synchronized void load(AnimalPair pair) {
            loadedAnimals.add(pair);
        }

        public synchronized boolean hasAminal(Species s) {
            for (AnimalPair ap : loadedAnimals) {
                if (ap.one.species.compareTo(s) == 0) {
                    return true;
                }
            }
            return false;
        }
    }

}