Synchronizing two Java-Thread loops

Two threads work in parallel:

Thread1:

while(...) {
  <-- wait until thread2 is not in update()
  doWork();
}

      

Thread2:

while(...) {
  doWork();
  <-- wait until thread1 is not in work()
  update();
}

      

I think the above examples explain what I'm trying to do, but I don't know how to do it. update()

-method thread2

is critical and thread2

must wait at runtime .

Edit:

Thanks for answers. More than one works well. I was asked what I am trying to do and I want to give a short update for this.

Based on currentState

thread2 computes nextState

and replaces both before it repeats the computation indefinitely. thread1 displays "currentState" in the gui for the user.

Thread1 should not display currentState

while the exchange is in progress .

That's all.

+3


source to share


3 answers


One way is to use locks (look at the package java.util.concurrent.locks

), but I first thought about if the whole approach could be improved, eg. with blocking algorithms, etc.

Another way could be using methods / blocks synchronized

on a shared object. However, it all depends on what you are actually trying to achieve.

An example of using a block synchronized

:

T1:

while(...) {
  synchronized( SomeClass.class ) {
    doWork();
  }
}

      

T2:

while(...) {
  doWork();
  synchronized( SomeClass.class ) {
    update();
  }
}

      

This is where you have to sync the same instance Class<SomeClass>

, which will run as long as you use the same classloader.

Keep in mind that you should make as few synchronized blocks as possible so as not to add unnecessary blocking.

Also, note that a synchronized block in T1 can make it difficult to break T2 between two iterations. However, the question is why it is designed this way.

Edit



As an alternative to syncing the entire call, doWork()

you might think about what actually needs to sync.

Take the following pseudocode as an example:

  WorkResult doWork(SharedObject so) {
    Data data = so.loadData();
    WorkResult wr = doSomeLengthyWork(data);
    return wr;
  }

  void update(WorkResult wr, SharedObject so) {
    so.updateFromWorkResult( wr );
  }

      

If your situation is like this, you can simply sync the calls to so.loadData()

and so.updateFromWorkResult()

and let the long run run on a copy of the data provided so.loadData()

.

Edit 2:

Alternatively, you can use the implementation ReadWriteLock

:

T1:

while(...) {
  Lock readlock = readWriteLock.readLock();
  readlock.lock();

  doWork();

  readlock.unlock();  
}

      

T2:

while(...) {
  doWork();

  Lock writelock= readWriteLock.writeLock();
  writelock.lock();

  update();

  writelock.unlock();
}

      

Please note that I left out exception handling, etc. for simplicity.

What you are basically doing here is grabbing a read lock at runtime and a write lock on update. Multiple threads can run in parallel if there is no write lock (multiple read locks are allowed) and updates will wait until all read locks are released. To do this, you can use ReentrantReadWriteLock

in fair mode which should trigger locks in the order in which they are requested, i.e. When T2 asks for a lock and T1 is still reading, it will get the next lock, even though T1 immediately requests to lock again.

+1


source


Using Lock

would be the simplest approach. Here's a demonstration of how simple it is. We create two Runnable

and run them under two threads. We then wait 30 seconds and then interrupt and wait for them to complete.

// Lock shared between both threads.
final Lock lock = new ReentrantLock();
// Random numbers.
final Random random = new Random();

public void test() throws InterruptedException {
    // Process 1 is a runnable.
    Runnable p1 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    // Grab the lock.
                    lock.lock();
                    // Do my work.
                    doWork();
                } catch (InterruptedException ex) {
                    System.out.println("P1 Interrupted!");
                    break;
                } finally {
                    // Release the lock in a `finally` to ensure it can never be left locked.
                    lock.unlock();
                }
            }
        }

        private void doWork() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P1 Working ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P1 Work done");
        }

    };

    Runnable p2 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    // Do my work.
                    doWork();
                    // Grab the lock.
                    lock.lock();
                    // Do my update.
                    update();
                } catch (InterruptedException ex) {
                    System.out.println("P2 Interrupted!");
                    break;
                } finally {
                    lock.unlock();
                }
            }
        }

        private void doWork() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P2 Working ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P2 Work done");
        }

        private void update() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P2 Update ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P2 Update done");
        }

    };

    // Create the two threads.
    Thread t1 = new Thread(p1);
    Thread t2 = new Thread(p2);
    // Start them up.
    t1.start();
    t2.start();
    // Wait 30 seconds - with narrative.
    for (int i = 0; i < 30; i++) {
        Thread.sleep(1000);
        System.out.println("Tick");

    }
    // Stop them.
    t1.interrupt();
    t2.interrupt();
    // Wait for them to stop.
    t1.join();
    t2.join();

}

      



Running this should demonstrate one of the problems with your design. Note that it is P2 Update

rarely called. This is due to the fact that it P1

spends very little time with the lock turned off and thus another thread is starving. See how in P1

it will lock.unlock()

, and then almost immediately do it lock.lock()

again when it starts the loop again.

+3


source


This is pseudocode, indeed, but I hope the idea is clear.

ExecutorService es = ...;
Future t1work = new Future(...);
Future t2work = new Future(...);
Future t2update = new Future(...);

es.submit(t1work);
es.submit(t2work);

while (1) {
  if (t1work.isDone() && t2work.isDone())   es.submit(t2update);
  if (t1work.isDone() && t2update.isDone()) es.submit(t1work);
  if (t2work.isDone() && t2update.isDone()) es.submit(t2work);
  Thread.sleep(100);
}

      

0


source







All Articles