1

I've got the following code pieces , containing 2 different methods that both methods will be accessed by many threads (getWeapon() and returnWeapon()).

Please anyone who can answer some or all of the following questions: 1. How can I make it as efficient as possible? 2. Can I not use the synchronized block? 3. Is it better to use a different Object as the key to the synchronizd block? 4. Is it better to use ReentrantLock/ReadWriteLock to handle this cuncurrent multi-threading cases?

private static final int M16_NUM_WEAPONS = 2;
private static final int AK47_NUM_WEAPONS = 5;
private static final int UZI_NUM_WEAPONS = 9;

private Map<Class<? extends Weapon>, Integer> WeaponsToAmountMap;

public Arsenal() {
    this.synchronizedWeaponsToAmountMap = new ConcurrentHashMap<Class<? extends Weapon>, Integer>();
}

public void initializeWeapons() {
    synchronizedWeaponsToAmountMap.put(M16.class, M16_NUM_WEAPONS);
    synchronizedWeaponsToAmountMap.put(AK47.class, AK47_NUM_WEAPONS);
    synchronizedWeaponsToAmountMap.put(Uzi.class, UZI_NUM_WEAPONS);
}

public Weapon getWeapon(Fighter fighter) {
    List<Class<? extends Weapon>> allowedWeapons = new ArrayList<>(fighter.getAllowedWeapons());

    Class<? extends Weapon> weaponClass = null;
    for (Class<? extends Weapon> allowedWeapon : allowedWeapons){
        synchronized (this) {
            Integer amount = synchronizedWeaponsToAmountMap.get(allowedWeapon);
            if (amount != null && amount > 0) {
                synchronizedWeaponsToAmountMap.put(allowedWeapon, amount - 1);
                System.out.println("Taking : "+allowedWeapon.getSimpleName());
                weaponClass = allowedWeapon;
                break;
            }
        }
    }
    if (weaponClass==null){
        return null;
    }

    Weapon weapon = null;
    try {
        weapon =  weaponClass.newInstance();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return weapon;
}

public void returnWeapon(Weapon weapon) {
    if (weapon==null){
        return;
    }
    synchronized(this) {
        System.out.println("returning : "+weapon.getClass().getSimpleName());
        synchronizedWeaponsToAmountMap.put(weapon.getClass(), synchronizedWeaponsToAmountMap.get(weapon.getClass()) + 1);
    }
}
oved mani
  • 19
  • 2
  • Please note: questions that ask to *review* *working* code should go to codereview.stackexchange.com ! – GhostCat Mar 22 '18 at 14:43
  • @GhostCat Not sure OP wrote this code himself. On top of that, it reads like a feature request instead of a request for review. So, I'm not so sure it should go there. – Mast Mar 22 '18 at 14:46
  • The problem is ConcurrentHashMap, Integer>. Using java.lang.Class hashCode is always bad idea. It is better to use something like `ConcurrentHashMap. weaponsToAmountMap.put(MyWeapon.class.getName(), 1);` If you will use `Collection.syncrhronizedList` you can avoid synchronized block at all. – Victor Gubin Mar 22 '18 at 14:46
  • You may find a [ReadWriteLock](https://stackoverflow.com/a/18354623/823393) more efficient. – OldCurmudgeon Mar 22 '18 at 15:30

1 Answers1

1
  1. I think it doesn't matter much different whether your synchronization done on one or different monitor objects: as soon as you want to prevent different threads access the same shared data simultaneously you use synchronization and it's linearize the access and then increase the time.

  2. The java.util.concurrent functionality generally and Reentrant/ReadWriteLock particularly use non-blocking approach and then might be faster -- but would be or not completely depends on how your threads interact with shared data...

Alex Evseenko
  • 122
  • 1
  • 5
  • My purpose here is to have multi-threading access for 2 methods: 1) getWeapon() - I want Threads to first fetch the Integer which represents the amount for each type of key (weapon), and right after to decrease it on the HashMap only if the current value was greater than 0. 2) returnWeapon() - first get the current amount by given key (weapon), after that to increase that amount value and put in the HashMap, So ReentrantLock might be better for this porpuse or synchronized(this) would do ? – oved mani Mar 23 '18 at 08:39
  • Using of ReentrantLock may prevent possible starvation of threads, as you can manage a time-out while acquiring a lock. But it would be important only for very big amount of threads (millions?). – Alex Evseenko Mar 23 '18 at 09:08