I need to create multiple tasks, each of that executes every n seconds. I've decided to use ScheduledExecutorService to schedule task execution. The problem is tasks not executed in time. I thought the reason is not enough processor time, but actual CPU usage is about 4-5 percents.
My schedulers creator:
class SchedulersCreator {
private final ScheduledExecutorService scheduler
= Executors.newScheduledThreadPool(1);
public SchedulersCreator(int tasksAmount, int repeatCount) {
for (int taskId = 0; taskId <= tasksAmount; taskId++) {
// create new task, that executes every 2 seconds
MyTask task = new MyTask(scheduler, repeatCount, 2, taskId);
// execute new task
task.run();
}
}
public static void main(String[] args) {
System.out.println("Program started");
// create & start 10 tasks, each of the executes 10 times with period 2 seconds
SchedulersCreator scheduler = new SchedulersCreator(10, 10);
System.out.println("All tasks created & started");
}
}
My task:
class MyTask implements Runnable {
// number of executions
private int executesTimesLeft;
// execution period
private final int periodSeconds;
// task id
private final int id;
// scheduler
private ScheduledExecutorService scheduler;
// field to measure time between executions
private long lastExecution = 0;
public MyTask(ScheduledExecutorService scheduler, int executes, int periodSeconds, int id) {
this.executesTimesLeft = executes;
this.id = id;
this.periodSeconds = periodSeconds;
this.scheduler = scheduler;
}
private void performAction() {
long before = System.currentTimeMillis();
long time = (before - lastExecution) % 1_000_000;
lastExecution = before;
// Simulates useful calculations
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
long after = System.currentTimeMillis();
if (id % 100_000 == 0) {
long duration = after - before;
System.out.println("Time since prev execution:\t" + time + "\t"
+ "Task " + id + ": "
+ executesTimesLeft + " executions lefts; "
+ "current duration\t" + duration);
}
}
#Override
public void run() {
// perform useful calculation in another thread
new Thread(() -> performAction()).run();
executesTimesLeft--;
if (executesTimesLeft > 0) { // schedule next task execution
scheduler.schedule(this, periodSeconds, SECONDS);
}
}
}
The code at the ideone: https://ideone.com/s3iDif.
I've expected time between executions about 2 seconds, but the actual result is 3-4 seconds.
Program output:
...
Time since prev execution: 3028 Task 0: 2 executions lefts; current duration 1000
Time since prev execution: 4001 Task 0: 1 executions lefts; current duration 1001
Your code doesn't use the scheduler properly.
// perform useful calculation in another thread
new Thread(() -> performAction()).run();
This doesn't actually run the code in a new thread. To do that you need to call start(), not run(). Calling run() makes the code execute in the current thread, no different than if you had just written performAction();.
However, you shouldn't be explicitly creating a new thread at all. You can and should do the work right in MyTask.run().
Tasks don't need to know about the scheduler or their frequency. Change this code:
MyTask task = new MyTask(scheduler, repeatCount, 2, taskId);
// execute new task
task.run();
to:
MyTask task = new MyTask(repeatCount, taskId);
Future<?> future = scheduler.scheduleAtFixedRate(task, 0, 2, SECONDS);
You want the task to repeat, so use the scheduler method that does so. That'll allow the scheduler to adjust the time in between tasks based on how long they take to run.
Move all of performAction() into MyTask.run(). When you want the task to stop repeating, use the future to cancel it.
Related
I need a way to delay a thread for a precise number of milliseconds (a music score playing application). I know that the precision of Thread.sleep() is not very good, so I decided to instead use ScheduledExecutorService. The way I do this is below:
... //some code
int duration = 100; //delay for 100ms
CountDownLatch l = new CountDownLatch(1);
Executors.newScheduledThreadPool(1).schedule(l::countDown, duration, TimeUnit.MILLISECONDS);
l.await();
... //continue execution
Is this a good approach? I am mostly worried about the CountDownLatch, and any delay (if any) it may add.
Thanks.
So your solution is not good because of a few issues. You're losing the advantage you gain from using a scheduled executor service by using await await is going to put the current thread to sleep, and then the os will need to schedule it again before it starts.
I've made an example using three different techniques. A spin wait, using thread.sleep and using your scheduled executor idea. The spin wait is the most accurate, at 7002ms where the other two solutions are over 8300ms when finished.
import java.util.concurrent.*;
public class DwellTimes{
static public void spinWait(long ms){
long end = System.nanoTime() + ms*1000000;
long current = System.nanoTime();
while( current < end){
current = System.nanoTime();
}
}
static public void sleepWait(long ms){
try{
Thread.sleep(ms);
} catch(Exception e){
throw new RuntimeException(e);
}
}
static ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
static public void scheduleWait(long ms){
try{
CountDownLatch l = new CountDownLatch(1);
ses.schedule(l::countDown, ms, TimeUnit.MILLISECONDS);
l.await();
} catch(Exception e){
throw new RuntimeException(e);
}
}
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i = 0; i<1000; i++){
scheduleWait(7);
}
long end = System.currentTimeMillis() - start;
System.out.println( end + "ms elapsed");
}
}
For a sleep/wait style of flow, the spin wait will be the most accurate because it doesn't release the Thread. It's just going to continue running hot.
The problem with your scheduled executor example is that you're not actually using the scheduling. You want to schedule your tasks.
public static void scheduleWork(){
CountDownLatch latch = new CountDownLatch(1000);
ses.scheduleAtFixedRate(latch::countDown, 7, 7, TimeUnit.MILLISECONDS);
try{
latch.await();
} catch(Exception e){
throw new RuntimeException(e);
}
}
This last example is probably the best way to manage consistent timing because you won't continue to accumulate errors. Eg, if your action takes a couple ms, the next action will not be delayed unless those couple of ms exceed the period.
public class FutureGetTimeoutTest {
private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws InterruptedException, ExecutionException {
List<String> respList = new ArrayList<String>();
List<Future<String>> futures = new ArrayList<Future<String>>();
futures.add(THREAD_POOL.submit(new CallableTask(1L)));
futures.add(THREAD_POOL.submit(new CallableTask(2L)));
futures.add(THREAD_POOL.submit(new CallableTask(3L)));
long start = System.currentTimeMillis();
System.out.println(start);
for (Future<String> future : futures) {
try {
respList.add(future.get(10000, TimeUnit.MILLISECONDS));
/*
* Timeout time for 2nd Task starts only at the end of 1st Task Timeout
* and so 2nd task is able to run for 20s and 3rd task for 30s!
*/
} catch (TimeoutException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end);
System.out.println(end - start);
System.out.println(respList);
}
}
class CallableTask implements Callable<String> {
private long ipAddressL;
public CallableTask(long ipAddressL) {
this.ipAddressL = ipAddressL;
}
#Override
public String call() throws Exception {
if (ipAddressL == 1) {
Thread.sleep(10000);
/* Imagine a DB operation taking more time. */
return "1";
} else if (ipAddressL == 2) {
Thread.sleep(20000);
return "2";
} else {
Thread.sleep(30000);
return "3";
}
}
}
I want to return a empty string or just terminate the thread or throw a TimeoutException from within each task if each task takes more than 10 seconds individually.
Say 1st thread takes 10s, Future.get() will wait for 10s and then timeout. I will catch the exception and proceed the iteration for 2nd future object. Say 2nd thread is not completed by this time (which means it ran for 10s while 1st thread ran and is still running), now Future.get() on 2nd thread will wait for another 10s and so a total of 20s and so on for subsequent threads.
future.get(1000, TimeUnit.MILLISECONDS) (1 sec),
will ensure 10s limit for the whole operation but I need a 10s limit on the whole operation by having a 10s limit on each individual concurrent task.
Use THREAD_POOL.invokeAll instead of submit to wait 10s for the tasks to complete.
If some of the tasks have completed before 10 seconds pass, you can check for that with future.isDone() and retrieve the result without blocking using future.get.
I am trying to come up with a concept where in the GUI I can give a query and specify the Pacing and duration.Like I give it 2 mins and 5hrs..which translates like for every 2mins run the given query for the next 5hrs..Though my GUI is ready and able to submit a query...I am unable to bring in the above specified time concept..Though I am sure this can be done through threading...I am finding it difficult to code it...can anyone over here help me with a basic approach to solve this issue ? is there a better and easy approach than threading ? please help..
Taking your example, if you have a query that you want to run every 2 minutes for 5 hours, what you need to do is calculate the number of times you want the query to execute.
2 minutes = 120 seconds.
5 hours = 18,000 seconds.
Number of times (iterations) = 18,000 / 120 = 150 iterations.
Therefore, you would need to submit the query 150 times, every 120 seconds (or 120,000 milliseconds).
You have to use threads if you want to submit more than one query with your GUI.
Edited to add: Based on assylias's comment, here's some code
public void runQuery(int interval, int duration) {
final Runnable query = new Runnable() {
#Override
public void run() {
// Run query
}
};
ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(1);
final ScheduledFuture<?> queryHandle = scheduler.scheduleAtFixedRate(
query, 0, interval, TimeUnit.SECONDS);
scheduler.schedule(new Runnable() {
#Override
public void run() {
queryHandle.cancel(true);
}
}, duration, TimeUnit.SECONDS);
}
Just using Thread, here's another version.
public void runQuery(int interval, int duration) {
final Runnable query = new Runnable() {
#Override
public void run() {
// Run query
}
};
int iterations = duration / interval;
for (int i = 0; i < iterations; i++) {
new Thread(query).start();
for (int j = 0; j < interval; j++) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
}
}
}
}
The Runnable query is just a place holder in these code examples.
You going to have to write a class that implements Runnable and passes the results of your query back to the GUI. Since you're not on the Swing Event Dispatch thread, you'll have to use the SwingUtilities.invokeLater() method to actually make GUI changes.
You would pass an instance of your class to one of these threaded methods to be executed for the interval and duration that you pass.
Problem Statement is:-
Each thread uses unique ID between 1 and 1000 and program has to run for 60 minutes or more, So in that 60 minutes it is possible that all the ID's will get finished so I need to reuse those ID's again,
I know several ways to do it, one way is the below that I wrote by taking help from StackOverflow, but when I tried running this, what I found is that, after few minutes of run this program gets very slow and it takes lot of time to print the ID on the console. And also I get OutOfMemory Error sometimes. Is there any better way to solve this kind of problem?
class IdPool {
private final LinkedList<Integer> availableExistingIds = new LinkedList<Integer>();
public IdPool() {
for (int i = 1; i <= 1000; i++) {
availableExistingIds.add(i);
}
}
public synchronized Integer getExistingId() {
return availableExistingIds.removeFirst();
}
public synchronized void releaseExistingId(Integer id) {
availableExistingIds.add(id);
}
}
class ThreadNewTask implements Runnable {
private IdPool idPool;
public ThreadNewTask(IdPool idPool) {
this.idPool = idPool;
}
public void run() {
Integer id = idPool.getExistingId();
someMethod(id);
idPool.releaseExistingId(id);
}
private void someMethod(Integer id) {
System.out.println("Task: " +id);
}
}
public class TestingPool {
public static void main(String[] args) throws InterruptedException {
int size = 10;
int durationOfRun = 60;
IdPool idPool = new IdPool();
// create thread pool with given size
// create thread pool with given size
ExecutorService service = new ThreadPoolExecutor(size, size, 500L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10), new ThreadPoolExecutor.CallerRunsPolicy());
// queue some tasks
long startTime = System.currentTimeMillis();
long endTime = startTime + (durationOfRun * 60 * 1000L);
// Running it for 60 minutes
while(System.currentTimeMillis() <= endTime) {
service.submit(new ThreadNewTask(idPool));
}
// wait for termination
service.shutdown();
service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
}
}
I already explained you in your previous question that your code submitted millions and millions of tasks to the executor, since it submits tasks in a loop during 60 minutes, withot waiting.
It's very unclear what your end goal is, but as is, you're filling a queue of tasks until you don't have any memory available anymore. Since you don't explain the goal of your program, it's hard to give you any solution.
But the first thing you could do is to limit the size of the task queue of your executor. This would force the main thread to block each time the queue is full.
What happens in the following case?
Timer t = new Timer();
t.schedule(...);
t = new Timer();
Specifically, what happens to the the tasks that I've scheduled on Timer t after I've assigned a new instance of Timer to t?
They doesn't go away. Each Timer object is associated with a background process. Even when you remove all references to your Timer in your program, the background process will still continue to run (it holds it's own reference to the object). Because of this, the object will not be subject to garbage collection.
See the official documentation for details.
Corresponding to each Timer object is a single background thread that is used to execute all of the timer's tasks, sequentially ... After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur.
It will run without any problems. The only thing is that if you won't be able to cancel the first timer (if you actually want to cancel it)
The API docs for Timer lead me to believe that losing a reference to the timer will not affect it at all. It appears that any scheduled tasks will still definitely be executed. The timer instance cannot be garbaged collected and the app cannot shut down until the last task scheduled with that Timer has finished executing. Excerpt from the docs:
"After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur. By default, the task execution thread does not run as a daemon thread, so it is capable of keeping an application from terminating. If a caller wants to terminate a timer's task execution thread rapidly, the caller should invoke the timer's cancel method."
for example
private ScheduledExecutorService scheduler;
private AccurateScheduledRunnable periodic;
private ScheduledFuture<?> periodicMonitor;
private int taskPeriod = 30;
private SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
private SimpleDateFormat sdfHour = new SimpleDateFormat("HH");
.
.
.
scheduler = Executors.newSingleThreadScheduledExecutor();
periodic = new AccurateScheduledRunnable() {
private final int ALLOWED_TARDINESS = 200;
private int countRun = 0;
private int countCalled = 0;
#Override
public void run() {
countCalled++;
if (this.getExecutionTime() < ALLOWED_TARDINESS) {
countRun++;
dateNext = new java.util.Date();
dateLast = new java.util.Date();
long tme = dateNext.getTime();
tme += (taskPeriod * 60) * 1000;
dateNext.setTime(tme);
//System.out.println("");
//System.out.println("");
//System.out.println("Next Sheduled Time at : " + sdf.format(dateNext));
//System.out.println("Periodic Cycle In : " + (countRun) + "/" + countCalled + " at " + sdf.format(dateLast));
//ti.displayMessage(null, " Running Sheduled Task at " + sdf.format(new Date()), TrayIcon.MessageType.NONE);
distAppInfo();
}
}
};
periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.MINUTES);
periodic.setThreadMonitor(periodicMonitor);
and implements Monitor that's returns f.e. remaining time to the next Shedule
long she = periodicMonitor.getDelay(TimeUnit.SECONDS);
and Monitor
abstract class AccurateScheduledRunnable implements Runnable {
private ScheduledFuture<?> thisThreadsMonitor;
public void setThreadMonitor(ScheduledFuture<?> monitor) {
this.thisThreadsMonitor = monitor;
}
protected long getExecutionTime() {
long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
return delay;
}
}
It would depend on which Timer.schedule(..) method that you plan to utilize. If the timer is set to repeatedly execute then assigning a new instance of Timer to t will not cause garbage collection as the timer thread will remain active.If you set the timer for one-time execution then the object will get garbage collected..at least that's what the documentation says..