Search code examples
javaandroidmultithreadingrunnablethreadpoolexecutor

ThreadPoolExecutor : Get a specific Runnable that is being executed


I am using ThreadPoolExecutor to execute multiple long running tasks in background, pool size of ThreadPoolExecutor is 4 so when more than 4 task are being added they are pushed on to queue and when one of 4 tasks gets complete one task is poped from queue for execution.

I want to know is there any way to access object of Runnable that are currently being executed and not in queue, i.e first 4 tasks.

Aim: I want to do this to get current status of task at any given point, with the help of mThreadPoolExecutor.getQueue() I am accessing tasks being queued and ready to execute, please suggest me way to access tasks those are currently being executed so that I can attach and remove listener/handler on it when ever required.

My Runnable class:

public class VideoFileUploadRunner implements Runnable {

    private final VideoFileSync mVideoFileSync;
    private final DataService dataService;

    private Handler handler;

    public VideoFileUploadRunner(VideoFileSync videoFileSync, DataService dataService) {
        this.mVideoFileSync = videoFileSync;
        this.dataService = dataService;

    }

    public int getPK()
    {
        return  mVideoFileSync.get_idPrimaryKey();
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        try {

            if (mVideoFileSync.get_idPrimaryKey() < 0) {
                addEntryToDataBase();
            }
            updateStatus(VideoUploadStatus.IN_PROGRESS);
            FileUploader uploader = new FileUploader();
            updateStatus(uploader.uploadFile(mVideoFileSync.getVideoFile()));



        } catch (Exception e) {
            updateStatus(VideoUploadStatus.FAILED);
            e.printStackTrace();
        }
    }

    private void addEntryToDataBase() {
        int pk = dataService.saveVideoRecordForSync(mVideoFileSync);
        mVideoFileSync.set_idPrimaryKey(pk);
    }

    private void updateStatus(VideoUploadStatus status) {
        if (handler != null) {
            Message msg = new Message();
            Bundle b = new Bundle();
            b.putString(AppConstants.Sync_Status, status.toString());
            msg.setData(b);
            handler.sendMessage(msg);
        }
        dataService.updateUploadStatus(mVideoFileSync.get_idPrimaryKey(), status.toString());


    }
} 

In Task progress list view holder:

public void setData(VideoFileSync fileSync) {
        tvIso.setText(fileSync.getVideoFile().getISO_LOOP_EQUP());
        tvUnit.setText(fileSync.getVideoFile().getUnit());
        tvName.setText(fileSync.getVideoFile().getLocalPath());
        tvStatus.setText(fileSync.getCurentStatus().toString());
        addHandleForUpdate(fileSync);
    }

    private void addHandleForUpdate(VideoFileSync fileSync) {

        Handler.Callback callBack = new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if(msg.getData()!=null)
                {
                    tvStatus.setText(msg.getData().getString(AppConstants.Sync_Status));

                }
                return false;
            }
        };
        mHadler = new Handler(Looper.getMainLooper(),callBack);

        VideoFileUploadRunner runner = VideoUploadManager.getInstance().getRunnerForSyncFile(fileSync);
        if(runner!=null)
        runner.setHandler(mHadler);
    }

in VideoUploadManager I have following method to return Runnable object, Here I want help so that I can return tasks being currently executed.

public synchronized VideoFileUploadRunner getRunnerForSyncFile(VideoFileSync fileSync) {
        Iterator<Runnable> itr = mThreadPoolExecutor.getQueue().iterator();
        while (itr.hasNext()) {
            VideoFileUploadRunner runner = (VideoFileUploadRunner) itr.next();
            if (runner.getPK() == fileSync.get_idPrimaryKey()) {
                return runner;
            }
        }
        return null;

    } 

Solution

  • The best way is to expose a synchronized variable holding the informations on currently executing tasks.

    public MyTask implements Runnable {
        private String id;
        private Map<String, MyTask> mapTasks;
    
        public MyTask(String id, Map<String, MyTask> mapTasks) {
            this.id = id;
            this.mapTasks = mapTasks;
        }
    
        public void run() {
             synchronized(mapTasks) {
                 mapTasks.put(id, this);
             }
    
             ...
    
             synchronized(mapTasks) {
                 mapTasks.remove(id);
             }
        }
    }
    
    
    // Create a map of tasks
    Map<String, MyTask> mapTasks = new HashMap<String, MyTask>();
    
    // How to create tasks
    MyTask myTask1 = new MyTask("task1", mapTasks);
    MyTask myTask2 = new MyTask("task2", mapTasks);
    
    executorService.execute(myTask1);
    executorService.execute(myTask2);
    
    ....
    

    And to print the list of tasks currently in execution:

    public void printCurrentExecutingTasks(Map<String, MyTask> tasks) {
        for (String id: tasks.keySet()) {
            System.out.println("Executing task with id: " + id);
        }
    }