user63898
user63898

Reputation: 30915

Java how to call or notify the calling Class when job is done?

I have simple calling to thread which do simple job. when condition is met
i want to invoke or notify the Base Class ( the one that started the thread )
that the job is done .
what is the best way to do this ?

class TaskThread implements Runnable 
{

    private int result = -1;
    public TaskThread()
    {

    }    
    //must be thread safe
    public synchronized int getResult()
    {
        return result;
    }

    @Override
    public void run() {

        if(result==-1 || result < 0)
        {
            StartFoo();
            result  = InvokeAPI();
            if(result == 1)
            {
            // HERE i like to do something to notify the outside world that the job is done ...  
            }

        }
    }
}


//Base class 
public class ScheduledThreadsManager {

    private static ScheduledThreadsManager instance = null;
    private ScheduledExecutorService scheduler;
    private Runnable pTaskThread = null ;
    protected  ScheduledThreadsManager() 
    {

    }

    //must be set only once in the app life cycle
    public void Init(int corePoolSize,
                       long TimeUnitINSeconds)
    {
            pTaskThread = new TaskThread();
            scheduler = Executors.newSingleThreadScheduledExecutor();
            scheduler.scheduleAtFixedRate(pTaskThread,1,TimeUnitINSeconds,TimeUnit.SECONDS);
    }
    public int getResult()
    {
        return pTaskThread.;
    }
    // Runtime initialization
    // By defualt ThreadSafe
    public static ScheduledThreadsManager getInstance() {
        if(instance == null){
            synchronized (ScheduledThreadsManager.class) {
                if(instance == null){
                    instance = new ScheduledThreadsManager();
                }
            }
        }
        return instance;
    }


    public void ShutdownNow()
    {

        pTaskThread = null ;
        scheduler.shutdown();
    }
}

now this is how i call it :

//somewhere in the code 

ScheduledThreadsManager.getInstance().init(1,10);
int runThis = true;
while(runThis)
{
    int ifOne = ScheduledThreadsManager.getInstance().getResult()
    if(ifOne==1)
    {
        runThis = false;
        ScheduledThreadsManager.getInstance().ShutdownNow()
    }
}

im not sure is the best way , can i somehow can method from the TaskThread run method and update the ScheduledThreadsManager ?

Upvotes: 2

Views: 879

Answers (3)

Jaroslaw Pawlak
Jaroslaw Pawlak

Reputation: 5578

I think the best approach would be to give a task a callback function:

Runnable callback = () -> System.out.println("done!");

Then in your task just add callback.run(); at the end. Or if your task returns a value that you need to consume, use Consumer instead of Runnable.

Here is a working example:

public class Task implements Runnable {

    private final Consumer<String> callback;

    public Task(Consumer<String> callback) {
        this.callback = callback;
    }

    @Override
    public void run() {
        // do actual work here instead
        String result = new Random().nextInt(2) == 0 ? "a" : "b";

        // call a private method in Caller
        callback.accept(result);
    }

}

public class Caller {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.submit(new Task((result) -> print(1, result)));
        executorService.submit(new Task((result) -> print(2, result)));
        executorService.submit(new Task((result) -> print(3, result)));

        Thread.sleep(2000);
    }

    private static void print(int id, String result) {
        System.out.println("task " + id + " done, returned: " + result);
    }

}

This allows you to e.g. call private methods of Caller and your Task will know nothing about it.

Upvotes: 3

Andy Turner
Andy Turner

Reputation: 140484

Don't notify the calling object: use ExecutorService and Future.

You can submit a Runnable to an ExecutorService and get back a Future:

Future<?> future = executorService.submit(myRunnable);

Now you can call future.get() (optionally with a timeout), which will block until the Future has completed:

future.get();
future.get(5, TimeUnit.MINUTES);

This means that you don't need to introduce any new callback interfaces, or similar. You also don't need to call future.get() immediately: you can submit a whole pile of tasks, and call get later.

Note that if you submit a Callable<T> to the ExecutorService, you get back a Future<T>: this is useful if you want your long-running computation to return a value.

Guava extends the Future interface with ListenableFuture, which allows you to execute callbacks once the Future has completed. Java 8 also adds CompleteableFuture, but I'm still in the dark ages, so I've never used that :)

Upvotes: 3

Murat Karag&#246;z
Murat Karag&#246;z

Reputation: 37604

You could achieve it with a interface e.g.

interface SomethingToBeCalled{
void notfiy();
}

Your BaseClass extends this

ScheduledThreadsManager implements SomethingToBeCalled{
void notify() {...};
}

And you give your Task Constructor this Interface

public TaskThread(SomethingToBeCalled smth)
    {
      this.smth = smth;
    }

And you can now notify the BaseClass

 if(result == 1)
  {
  smth.notify();
  }

Upvotes: 2

Related Questions