Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

synchronized block - lock more than one object

I'm modelling a game where multiple players (threads) move at the same time. The information of where a player is located at the moment is stored twice: the player has a variable "hostField" that references to a field on the board and every field has an ArrayList storing the players that are currently located at this field.

I'm not very happy with the fact that I have redundant information, but I found no way avoiding that without looping through a big dataset.

However, when a player moves from one field to another, I'd like to make sure (1) the redundant information stays linked (2) nobody else is manipulating the field at the moment.

Therefore I need to do something like

synchronized(player, field) {     // code } 

Which is not possible, right?

What should I do? :)

like image 458
speendo Avatar asked Jan 05 '11 12:01

speendo


People also ask

Does synchronized method lock whole object?

When we use a synchronized block, Java internally uses a monitor, also known as monitor lock or intrinsic lock, to provide synchronization. These monitors are bound to an object; therefore, all synchronized blocks of the same object can have only one thread executing them at the same time.

What is true about lock in synchronized block?

Locks In Synchronized Methods When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.

Can synchronized block be nested?

In that kind of scenario both threads will be waiting for each other forever to release lock they are already holding thus creating a deadlock. One synchronized method is called from another synchronized method. See example. There are nested synchronized blocks.

Is synchronized block more efficient than method?

synchronized block has better performance as only the critical section is locked but synchronized method has poor performance than block. synchronized block provide granular control over lock but synchronized method lock either on current object represented by this or class level lock.


2 Answers

A trivial solution would be

synchronized(player) {     synchronized(field) {         // code     } } 

However, make sure that you always lock the resources in the same order to avoid deadlocks.

Note that in practice, the bottleneck is the field, so a single lock on the field (or on a dedicated, common lock object, as @ripper234 rightly pointed out) may suffice (unless you are concurrently manipulating players in other, conflicting ways).

like image 114
Péter Török Avatar answered Sep 21 '22 05:09

Péter Török


In fact, synchronization is for code, not objects or data. The object reference used as a parameter in synchronized block represent the lock.

So if you have code like:

class Player {    // Same instance shared for all players... Don't show how we get it now.   // Use one dimensional board to simplify, doesn't matter here.   private List<Player>[] fields = Board.getBoard();     // Current position   private int x;     public synchronized int getX() {     return x;   }    public void setX(int x) {     synchronized(this) { // Same as synchronized method       fields[x].remove(this);       this.x = x;       field[y].add(this);     }   } } 

Then Despite being in the synchronized block the access to field is not protected because the lock is not the same (it being on different instances). So your List of Players for your board can become inconsistent and cause runtime exceptions.

Instead if you write the following code, it will work because we have only one shared lock for all players:

class Player {    // Same instance shared for all players... Don't show how we get it now.   // Use one dimensional board to simplify, doesn't matter here.   private List<Player>[] fields;     // Current position   private int x;    private static Object sharedLock = new Object(); // Any object's instance can be used as a lock.    public int getX() {     synchronized(sharedLock) {       return x;     }   }    public void setX(int x) {     synchronized(sharedLock) {       // Because of using a single shared lock,       // several players can't access fields at the same time       // and so can't create inconsistencies on fields.       fields[x].remove(this);        this.x = x;       field[y].add(this);     }   } } 

Be sure to use only a single lock to access all the players or your board's state will be inconsistent.

like image 32
Nicolas Bousquet Avatar answered Sep 23 '22 05:09

Nicolas Bousquet