Reputation: 30915
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
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
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
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