Site Search:

Animals.java first attempt for multi-threading

<Back

Animals.java
The following program have several problems:
1. hasAnimal can throw ConcurrentModificationException when multiple threads call load and hasAnimal simultaneously.

2. the helper thread only start to work after the main thread finish loading the ark.
3. Set<AnimalPair> loadedAnimals can have duplicates.


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();
        
        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));
        
        //can start helper earlier
        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();
        
    }

    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 {
        // synchronized wrapper classes support client-side locking, using the wrapper collection (loadedAnimals)
        private final Set<AnimalPair> loadedAnimals = Collections.synchronizedSet(new HashSet<AnimalPair>());

        public void load(AnimalPair pair) {
            loadedAnimals.add(pair);
        }
        
        public boolean hasAminal(Species s) {
            for(AnimalPair ap: loadedAnimals) {
                //load can be changing the loadedAnimals while you are iterating
                if(ap.one.species.compareTo(s) == 0) {
                    return true;
                }
            }
            return false;
        }
    }

}