You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Andrus Adamchik <an...@objectstyle.org> on 2011/09/01 20:40:39 UTC

Re: Test & Set business logic using synchronized blocks

Synchronizing on a DataObject is not going to help across contexts as each context has its owen copy of an object. But you can create your own arbitrary lock object of course that is shared between all threads. If this has to be per-object lock, you may try using objects from a single shared ObjectContext, or build a concurrent Map of ObjectIds to use as locks.

Also since context sync events are propagated asynchronously, you will have to use the database as an authoritative source of data. E.g.:

synchronized(myLock) {

  context.invalidateObject(Collections.singleton(myObject));
  if (myObject.getEsInactivo()) {

  }
}

Andrus


On Aug 29, 2011, at 9:10 AM, Hans C. Poo wrote:

> Hi,
> 
> ¿ Is it possible to use Test & Set business logic using synchronized blocks in persistent instances ?
> 
> Each objectContext has its own instances, then there may be many copies of the same object identity (id) in memory, that keep in sync with each other if you commit, ¿ Is it possible to use normal java synchronized block  ?
> 
> I've build the next test using two threads, and when it runs, the field is modified twice, under normal java concepts, only one thread should succeed.
> 
> Note: Factory, is a repository with static methods to manage persistence (finally calls ObjectContext and DataContext).
> 
> 
> package cl.company.test;
> 
> import cl.company.domain.Usuario;
> import cl.company.domain.Factory;
> 
> /**
> * Concurrency over same instance, test & set. * @author hans
> *
> */
> public class TestConcurrenciaCayenne implements Runnable {
> 
> 	private static final int USER_ID = 1000;
> 	String name;
> 
> 	public TestConcurrenciaCayenne(String name) {
> 		this.name = name;
> 	}
> 
> 	public static void main(String[] args) {
> 
> 		Factory.createAndBindDataContext();
> 
> 		Usuario c = Factory.getUsuario(USER_ID);
> 		c.setEsInactivo(true);
> 		Factory.commit();
> 
> 		TestConcurrenciaCayenne t1 = new TestConcurrenciaCayenne("t1");
> 		TestConcurrenciaCayenne t2 = new TestConcurrenciaCayenne("t2");
> 		new Thread(t1).start();
> 		new Thread(t2).start();
> 	}
> 
> 	@Override
> 	public void run() {
> 
> 		Factory.createAndBindDataContext();
> 
> 		Usuario c = Factory.getUsuario(USER_ID);
> 		System.out.println("Before synchronized block " + this.name);
> 		synchronized (c) {
> 
> 			System.out.println("Inside the block " + this.name);
> 			if (c.getEsInactivo()) {
> 				try {
> 					Thread.sleep(2000);
> 				} catch (InterruptedException e) {
> 					e.printStackTrace();
> 				}
> 				c.setEsInactivo(false);
> 				System.out.println("Modifyin " + this.name);
> 			}
> 
> 			Factory.commit();
> 
> 		}
> 
> 	}
> 
> }
> 
> 
> 
> Thanks
> Hans
> 


Re: Test & Set business logic using synchronized blocks

Posted by Andrus Adamchik <an...@objectstyle.org>.
Also an improvement to that would be using some kind of LRU or weak/soft reference map with concurrent behavior, so that the ids map does not cause a memory leak as it fills with data. Java's ConcurrentHashMap doesn't have the LRU/weak/soft reference ability. If you have a big database, you may need to use Google Guava library to build a map with the desired behavior:

http://code.google.com/p/guava-libraries/
http://docs.guava-libraries.googlecode.com/git-history/v9.0/javadoc/com/google/common/collect/MapMaker.html

I haven't tried that myself, but there are lots of people who seem to be very happy with it.

Andrus


On Sep 9, 2011, at 9:01 AM, Andrus Adamchik wrote:

> class PerObjectLockManager {
> 
>   private ConcurrentMap<ObjectId, ObjectId > ids = ...
> 
>   Object objectLock(Persistent object) {
> 
>       ObjectId newId = object.getObjectId();
>       ObjectId existingId = ids.putIfAbsent(newId, newId);
>       return existing != null ? existingId : newId;
>   }
> }


Re: Test & Set business logic using synchronized blocks

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Sep 9, 2011, at 8:40 AM, Hans C. Poo wrote:

> I appreciate your answer, is really hard form me to imagine that synchronized, the natural java concurrency mechanism have not been requested before, optimistic or pessimistic locking fixes another situation, that sometimes may collide with test & set, but it's different.

Well, Cayenne is a part of a distributed system that at the minimum includes a database and an app server. Moreover Cayenne is being used in massive appserver clusters. So single VM Java "synchronized" can only go that far addressing a special case.

> With respect to your answer:
> Create your own arbitrary lock object of course that is shared between all threads. If this has to be per-object lock, you may try using objects from a single shared ObjectContext, or build a concurrent Map of ObjectIds to use as locks.
> 
> Is the use of a Custom Superclass a way to transparently implement this, implementing a method say sharedLock() ?
> 
> And in the code call something like:
> 
> synchronized(myObject.sharedLock())  {
> 
> 
> 
> }
> 
> May be in the core already exists a candidate shared representation of an single instance, that may serve for synchronize in...

So do you need one global lock or per-object lock? I assume the later. 

Here is a class that does all you need here. You can either make it a static addition to your DataObjects superclass, or (my own preferred way), keep it somewhere else in the application scope. In Cayenne 3.1 I'd put it in DI container.

class PerObjectLockManager {
 
    private ConcurrentMap<ObjectId, ObjectId > ids = ...

    Object objectLock(Persistent object) {

        ObjectId newId = object.getObjectId();
        ObjectId existingId = ids.putIfAbsent(newId, newId);
        return existing != null ? existingId : newId;
    }
}

Andrus







Re: Test & Set business logic using synchronized blocks

Posted by "Hans C. Poo" <ha...@welinux.cl>.
Andrus,

I appreciate your answer, is really hard form me to imagine that synchronized, the natural java concurrency mechanism have not been requested before, optimistic or pessimistic locking fixes another situation, that sometimes may collide with test & set, but it's different.

With respect to your answer:
Create your own arbitrary lock object of course that is shared between all threads. If this has to be per-object lock, you may try using objects from a single shared ObjectContext, or build a concurrent Map of ObjectIds to use as locks.

Is the use of a Custom Superclass a way to transparently implement this, implementing a method say sharedLock() ?

And in the code call something like:

synchronized(myObject.sharedLock())  {



}

May be in the core already exists a candidate shared representation of an single instance, that may serve for synchronize in...

Thanks
Hans

----- Mensaje original -----
De: "Andrus Adamchik" <an...@objectstyle.org>
Para: user@cayenne.apache.org
Enviados: Jueves, 1 de Septiembre 2011 15:40:39
Asunto: Re: Test & Set business logic using synchronized blocks

Synchronizing on a DataObject is not going to help across contexts as each context has its owen copy of an object. But you can create your own arbitrary lock object of course that is shared between all threads. If this has to be per-object lock, you may try using objects from a single shared ObjectContext, or build a concurrent Map of ObjectIds to use as locks.

Also since context sync events are propagated asynchronously, you will have to use the database as an authoritative source of data. E.g.:

synchronized(myLock) {

  context.invalidateObject(Collections.singleton(myObject));
  if (myObject.getEsInactivo()) {

  }
}

Andrus


On Aug 29, 2011, at 9:10 AM, Hans C. Poo wrote:

> Hi,
> 
> ¿ Is it possible to use Test & Set business logic using synchronized blocks in persistent instances ?
> 
> Each objectContext has its own instances, then there may be many copies of the same object identity (id) in memory, that keep in sync with each other if you commit, ¿ Is it possible to use normal java synchronized block  ?
> 
> I've build the next test using two threads, and when it runs, the field is modified twice, under normal java concepts, only one thread should succeed.
> 
> Note: Factory, is a repository with static methods to manage persistence (finally calls ObjectContext and DataContext).
> 
> 
> package cl.company.test;
> 
> import cl.company.domain.Usuario;
> import cl.company.domain.Factory;
> 
> /**
> * Concurrency over same instance, test & set. * @author hans
> *
> */
> public class TestConcurrenciaCayenne implements Runnable {
> 
> 	private static final int USER_ID = 1000;
> 	String name;
> 
> 	public TestConcurrenciaCayenne(String name) {
> 		this.name = name;
> 	}
> 
> 	public static void main(String[] args) {
> 
> 		Factory.createAndBindDataContext();
> 
> 		Usuario c = Factory.getUsuario(USER_ID);
> 		c.setEsInactivo(true);
> 		Factory.commit();
> 
> 		TestConcurrenciaCayenne t1 = new TestConcurrenciaCayenne("t1");
> 		TestConcurrenciaCayenne t2 = new TestConcurrenciaCayenne("t2");
> 		new Thread(t1).start();
> 		new Thread(t2).start();
> 	}
> 
> 	@Override
> 	public void run() {
> 
> 		Factory.createAndBindDataContext();
> 
> 		Usuario c = Factory.getUsuario(USER_ID);
> 		System.out.println("Before synchronized block " + this.name);
> 		synchronized (c) {
> 
> 			System.out.println("Inside the block " + this.name);
> 			if (c.getEsInactivo()) {
> 				try {
> 					Thread.sleep(2000);
> 				} catch (InterruptedException e) {
> 					e.printStackTrace();
> 				}
> 				c.setEsInactivo(false);
> 				System.out.println("Modifyin " + this.name);
> 			}
> 
> 			Factory.commit();
> 
> 		}
> 
> 	}
> 
> }
> 
> 
> 
> Thanks
> Hans
>