Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring synchronization issue

I know that question about reusing prototype beans was asked many times, but my question means more than this.

What is the problem:

I start asynchronous tasks in handler (prototype bean) in for-lookup. but I can't start the next asynchronous task before the previous reach some milestone. So I have to proceed for-loop only after the previous task invokes a special method.

What the questions:

  1. How to wait in for-loop before some method call in another bean?
  2. Can I invoke proceedLookUp() method of current prototype bean from another beans?

@Service(value = "Request.Start")
@Scope("prototype")
public static class Start {

  public Start() {}

  private Object lock;

  @Transactional
  public void handler(Request request, Response response) {

    for (int i = 0; i < request.getAmount(); i++) {
      Utils.asyncProcessStart(); //Can't start the next async process before the previous rich some defined milestone
      lock.wait();
    }
  }

  public void proceedLookUp() {
    lock.notify();
  }
}

@Service
public void AsynchronousTask {

  public void asyncAction() [
    //Needed logic, before start the next async task
    getStartHandler().proceedLookUp();
  }

  public void getStartHandler() {
    //HOW TO REWRITE NEEDED PROTOTYPE BEAN
  } 
}

ADDITION:

What is the problem: I use Activiti framework, which imply some restrictions. I should store some variables to a process (thread) context. I CAN write variables to a global context, BUT CAN'T write to a local process (thread) context before the process (thread) has been started.

what you expect to happen, say if request.getAmount() returns 2?

I should start two asynchronous processes in two different threads. Each process have the same set of variables. I must write appropriate variables to the local context of each process (thread). But, I CAN'T do it before the process (thread) is started (due to the specific of Activiti framework).

For example, each process (thread) should write "id" property to his own local context. I have List ids in handler method

So, I should do the next sequence of actions:

  1. Write ids.get(0) as "id"-property to GLOBAL context
  2. Start the first process
  3. Hang on the for-loop
  4. [Inside the first process] write "id" property from global to local context (it is possible inside the first process)
  5. Notify appropriate Start bean that it can continue the for-loop
  6. Write ids.get(1) as "id"-property to GLOBAL context
  7. Start the second process
  8. Hang on the for-loop
  9. [Inside the second process] write "id" property from global to local context (it is possible inside the second process)
  10. Notify appropriate Start bean that it can continue the for-loop

Why can't you just call it synchronously?

As you have already understood, there is no guarantee that the first process (thread) write the "id"-property to it local context, before it has been overriden by for-loop for the second process (thread).

like image 316
VB_ Avatar asked Aug 09 '26 03:08

VB_


1 Answers

Here's my suggestion: create a singleton object that you can share between your threads to pass information (state). The singleton uses a semaphore to coordinate between your threads. You can use this approach to pass the new thread's identity back to your Service class. Here's a simple example that shows what I'm proposing.

The test class:

public class TestSemaphore {

    @Test
    public void test() throws Exception {

        ThreadCoordinator tc = ThreadCoordinator.getInstance();

        for( int i = 0; i < 100; i++ ) {
            MyThread r = new MyThread();
            r.run();

            // This will block until the Thread has called release (after setting its identity on the ThreadCoordinator)    
            tc.acquire();
            String newThreadIdentity = tc.getIdentity();
            System.out.println( "Received the new thread's identity:         " + newThreadIdentity );

            // This will allow the next Thread to acquire the semaphore
            tc.release();
        }
    }


    class MyThread extends Thread {

        public void run() {
            String identity = Integer.toString( (int)(Math.random() * 10000) );
            System.out.println( "Running a new thread with identity:         " + identity );

            // Get a reference to the singleton
            ThreadCoordinator tc = ThreadCoordinator.getInstance();
            try {
                tc.acquire();
                tc.setIdentity( identity );
                System.out.println( "Notified the ThreadCoordinator from thread: " + identity );
                tc.release();
            } catch( InterruptedException e ) {
                System.out.println( "Caught an interrupted exception: " + e );
            }

        }

    }
}

The ThreadCoordinator (semaphore) class:

import java.util.concurrent.Semaphore;

public class ThreadCoordinator {

        private static ThreadCoordinator tc = new ThreadCoordinator();
        private static Semaphore semaphore = new Semaphore( 1, true );      
        private static String identity;

        // singleton get instance
        public static ThreadCoordinator getInstance() {
            return ThreadCoordinator.tc;
        }

        public void setIdentity( String identity ) throws InterruptedException {
            ThreadCoordinator.identity = identity;
        }


        public String getIdentity() throws InterruptedException {
            String identity = ThreadCoordinator.identity;
            ThreadCoordinator.identity = null;
            return identity;
        }

        public void acquire() throws InterruptedException {
            ThreadCoordinator.semaphore.acquire();
        }

        public void release() {
            ThreadCoordinator.semaphore.release();
        }

 }
like image 144
Alex Avatar answered Aug 11 '26 16:08

Alex



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!