user1588452
user1588452

Reputation: 33

Java - Stumped with threading puzzle

Basically, I am trying to implement a mechanism where I have two threads going in parallel. Thread1 is continuously updating a counter value. When the counter value reaches increments of specific values (ex. multiple of 100, 250, 500), then I want Thread2 to execute a specific task selected on the counter value in parallel. Thread1 should continue counting but it should not count past the key value if Thread2 has not completed its' task.

Use case: Thread 1 has updated the counter to be 100. This dispatches Thread2 to perform TaskA. Thread1 is counting still. The counter reaches 250. If Thread2 has finished its' task, Thread1 should continue. Otherwise, Thread1 should wait for TaskA to be finished before proceeding.

|t2             |t1   
|               | 
|               | 
|               | 
______100________ <----start thread 2 real quick
|               | 
|               | 
|               | 
|               | 
|               | 
|               | 
_______250______ <------at this point just wait for taskA to finish
|               |          IF it's not finished. If it is, start taskB and
|               |          continue counting
V               V

I've been hacking at the problem for a bit but I've scrapped everything so far. I'd appreciate code/pseudocode/hints/advice. Thanks in advance

Upvotes: 0

Views: 357

Answers (4)

anish
anish

Reputation: 492

The CyclicBarrier can be used to create a barrier where the threads would wait for the other thread. So, below, there are two threads 'countingThread' and 'taskThread'. The 'countingThread' would perform its counting and would invoke the 'await' when the counting has reached a specific point, (method-'checkBarrierCondition' below).

As per the example in the question, when the counting-thread reaches 100, it can call 'await' on the barrier and if the task-thread has completed its task by that time, the barrier would snap and both would proceed to next activities. If the task has not been completed yet, then the counter thread will wait for the task-performing thread.

All the locking is handled by CyclicBarrier and concurrent framework

public class Threading {

public void execute() {
    final CyclicBarrier barrier = new CyclicBarrier(2);

    Thread countingThread = new Thread(new Tasker(barrier));
    Thread taskThread = new Thread(new Counter(barrier));

    countingThread.start();
    taskThread.start();

    try {
        countingThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {

    new Threading().execute();

}

class Tasker implements Runnable {
    private CyclicBarrier barrier;

    Tasker(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    public void run() {
        String task = "taskA";      //just some mock-up task name

        while (!allTasksDone(task)) {
            task = performTask(task);
            try {
                System.out.println("Tasker : Await on barrier ");
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

        }
    }

}

class Counter implements Runnable {
    private CyclicBarrier barrier;

    Counter(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    public void run() {
        int counter = 0;  //just for the sake of example; starting at 0

        while (!isCountingDone(counter)) {
            counter = performCounting(counter);
            if (checkBarrierCondition(counter)) {
                try {
                    System.out.println("Counter : Await on barrier ");
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

}

Upvotes: 2

Suken Shah
Suken Shah

Reputation: 1682

I would suggest have a look at Java's executor service. It really abstracts most of the complexities associated with multiple threads. Plus you can easily increase number of threads executing tasks if required in future. Basically you run counting in your first thread. When you want to execute a task in another thread you simply create a callable. The API will return you a future(s) for your callable(s). When you have finished processing/counting in thread1 you simply call get or getValue on your future from thread1. Now the beauty of this is that it will return you the result immediately if other thread has finished processing. If other thread is busy processing the task then it will block your thread1 until result is returned. Please note that you don't need to do any locking, blocking or notifying manually. Don't forget to use threadsafe collections if you are sharing data between multiple threads. Hope this helps!

Upvotes: 0

314314314
314314314

Reputation: 259

package testRandomStuff;


public class ThreadingPuzzle {
    public int countMax = 25;
    public int factor = 5;
    public Thread threadA, threadB;

    private class Signal {
        public volatile boolean flag = true;

        public Signal(boolean initial) {
            flag = initial;
        }

        public synchronized void setFlag() {
            flag = true;
            notifyAll();
        }

        public synchronized void unsetFlag() {
            flag = false;
            notifyAll();
        }

        public synchronized boolean getFlag() {
            return flag;
        }
    }

    public Signal checkpoint = new Signal(true);
    public Signal doWork = new Signal(false);

    Runnable threadARunnable = new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < countMax; i++) {
                if (i % factor == 0) {
                    if (checkpoint != null) {
                        // --------mechanism to wait for threadB to finish---------
                        synchronized (checkpoint) {
                            try {
                                // -----use while loop to prevent spurious wakeup------
                                // Checkpoint flag is true in the first iteration, no need to wait.
                                while (!checkpoint.getFlag()) {
                                    checkpoint.wait();
                                }
                            } catch (InterruptedException ie) {
                                // handle exception
                            }
                        }
                        // ThreadB has finished last job when threadA leaves the above sync-block
                    }

                    // ------ start threadB real quick---------
                    // unset checkpoint flag, so that threadA will not proceed the next
                    // interation without threadB setting the flag first.
                    // send signal to threadB to wake it up
                    checkpoint.unsetFlag();
                    doWork.setFlag();

                }
                System.out.println("Thread A - count:"+i);
            }

        }
    };

    Runnable threadBRunnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                // --------mechanism to wait for threadA send job---------
                synchronized (doWork) {
                    try {
                        // -----use while loop to prevent spurious wakeup------
                        // doWork flag is false in the first iteration, wait for ThreadA.
                        while (!doWork.getFlag()) {
                            doWork.wait();
                        }
                    } catch (InterruptedException ie) {
                        // handle exception
                    }
                }

                doWork.unsetFlag();
                // -----------do what ever you need to do in threadB-----------
                System.out.println("Thread B - do some work");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ie) {

                }
                System.out.println("Thread B - done working");
                // ------------Finish work, notify threadA---------
                checkpoint.setFlag();
            }
        }

    };

    public ThreadingPuzzle() {
        // FIXME Auto-generated constructor stub
    }

    public static void main(String[] args){
        ThreadingPuzzle puzzle = new ThreadingPuzzle();
        puzzle.threadA = new Thread(puzzle.threadARunnable);
        puzzle.threadB = new Thread(puzzle.threadBRunnable);
        puzzle.threadA.start();
        puzzle.threadB.start();
    }

}


SIMULATION RESULTS
Thread B - do some work
Thread A - count:0
Thread A - count:1
Thread A - count:2
Thread A - count:3
Thread A - count:4
Thread B - done working
Thread B - do some work
Thread A - count:5
Thread A - count:6
Thread A - count:7
Thread A - count:8
Thread A - count:9
Thread B - done working
Thread B - do some work
Thread A - count:10
Thread A - count:11
Thread A - count:12
Thread A - count:13
Thread A - count:14
Thread B - done working
Thread B - do some work
Thread A - count:15
Thread A - count:16
Thread A - count:17
Thread A - count:18
Thread A - count:19
Thread B - done working
Thread B - do some work
Thread A - count:20
Thread A - count:21
Thread A - count:22
Thread A - count:23
Thread A - count:24
Thread B - done working
Thread B - do some work
Thread B - done working

Upvotes: 0

rmalchow
rmalchow

Reputation: 2769

you probably want to use locks? consider this - counter:

import java.util.concurrent.locks.Lock;


public class ThreadOne extends Thread {

    private ThreadTwo two;
    private Lock lock;

    public ThreadOne(Lock l, ThreadTwo two) {
        this.two = two;
        this.lock = l;
        this.start();

    }

    @Override
    public void run() {
        int i = 0;
        while(true) {
            if(i%100==0) {
                // tell other thread to start
                two.startRunning();
                while(two.pending()) {
                    // wait until it actually started
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            // acquire the lock (or wait)
            lock.lock();
            try {
                // count up
                i++;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        } 
    }

}

execution thread:

import java.util.concurrent.locks.Lock;


public class ThreadTwo extends Thread {

    private boolean pending = false;
    private Lock lock;

    public ThreadTwo(Lock l) {
        this.lock = l;
        this.start();
    }

    public void startRunning() {
        pending = true;
    }

    public boolean pending() {
        return pending;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
            }
            if(pending) {
                lock.lock(); 
                try {
                    pending = false;
                    execute();
                } catch (Exception e) {
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    private void execute() {
    }

}

and how to start them.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Main {

    public static void main(String[] args) {
        Lock l = new ReentrantLock();
        ThreadTwo two = new ThreadTwo(l);
        ThreadOne one = new ThreadOne(l,two);
    }

}

Upvotes: 0

Related Questions