简体   繁体   中英

Submit a task to an ExecutorService using a SheduleExecutorService

I'm developing a JavaFX application for read data from a serial device and show a notification when a new device is connected to the computer.

I have a task DeviceDetectorTask which scans all the ports and creates an event when a new device is connected. This task must be submited every 3 seconds.

When a device is detected the user can press a button to read all the data contained in it. This is performed by another task ReadDeviceTask . At this point and while the ReadDeviceTask is running scan operations should not be performed (I cannot read and scan one port at the same time). So only one of the two task can be running at a time.

My actual solution is:

public class DeviceTaskQueue {
    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    public void submit(Runnable task) {
        executorService.submit(task);
    }
}


public class ScanScheduler {
    private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public void start() {
        AddScanTask task = new AddScanTask();
        executor.scheduleAtFixedRate(task, 0, 3, TimeUnit.SECONDS);
    }
}


public class AddScanTask implements Runnable {
    @Autowired
    DeviceTaskQueue deviceTaskQueue;

    @Override
    public void run() {
        deviceTaskQueue.submit(new DeviceDetectorTask());
    }
}

public class ViewController {
    @Autowired
    DeviceTaskQueue deviceTaskQueue;

    @FXML
    private readDataFromDevice() {
        deviceTaskQueue.submit(new ReadDeviceTask());
    }
}

My question is: is it ok to add a task to the ExecutorService from the task AddScanTask which has been scheduled by the ScheduledExecutorService?

Yes, An Executor May Post Task To Another Executor

To answer your simple question in last line:

is it ok to add a task to the ExecutorService from the task AddScanTask which has been scheduled by the ScheduledExecutorService?

Yes. Certainly you can submit a Callable / Runnable from any other code. That the submitting code happens to be running from another executor is irrelevant, as code run from an executor is still “normal” Java code, just running on a different thread.

That is the whole point of the executor, to handle the juggling of threads in a manner convenient to you the programmer. Making multi-threaded coding easier and less error-prone is why these classes were added to Java. See the extremely helpful book, Java Concurrency in Practice by Brian Goetz et al. And see other writings by Goetz.

In your case you have two executors each with their own thread, each executing a series of submitted tasks. One has tasks submitted automatically (timed) while the other has tasks submitted manually (arbitrarily). Each executes on their own thread independent of one another. With multiple cores they may execute simultaneously.

Therein lies the bigger problem: In your scenario you don't want them to be independent . You want the reading tasks to block the scanning tasks.

Bigger Problem

The problem you present is that a regularly occurring activity (scanning) must halt when an arbitrary event (reading) happens. That means the two activities must coordinate with one another. The question is how to coordinate.

Semaphores

When the arbitrary event is happening, it should raise a flag. The recurring activity, when it runs, should always check for that flag. If raised, wait until the flag lowers before proceeding with scan. The ScheduledExecutorService is designed for this, tolerating a task that may run for a time longer than the scheduled period. If one execution of the task runs long, the SES does not run again, so it does not pile up a backlog of executions. That is just the behavior you want.

Vice versa, if the recurring activity is executing, it should raise a flag. The arbitrary event's first to-do item is to check for that flag. If raised, wait until lowered. Then proceed, first raising its own flag and then proceeding with the task at hand (scanning).

Perhaps your scenario should be designed with a single flag rather than scanner and reader each having their own. I would have to think about it more and probably know more about your scenario.

The technical term for such flags is semaphore .

Unfortunately your comment says you cannot alter the scanner's source code. So you cannot implement the semaphores and coordinate the activities. So I am stuck, cannot see a solution.

Hack

Given your frozen code, one hack solution, which I do not recommend, is that the regularly occurring activity (the scanning) not actually do the work but instead post a scanning task on another thread (another executor). That other executor would also be the same executor used to post the arbitrary activity (the reading). So there is one single queue of to-do items, a mix of scanning and reading jobs, submitted to a single-thread executor. The single-thread means they get done one at a time in sequence of their submission.

I do not like this hack because if any of the to-do items takes a long while you will begin to accumulate a backlog. That could be a mess.


By the way, no need for the DeviceTaskQueue in your example code. Just call the instance of the ExecutorService directly to submit a task. That is the job of an ExecutorService , and wrapping it adds no value that I can see.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM