Executors framework
As said in the Answer by kofemann, use threads to run code in the background. But handling threads directly is tricky business. So the Executors framework was added to Java to make this work easier.
In particular, you want a ScheduledExecutorService
to perform some task over and over. Use the Executors
class to get an instance of a ScheduledExecutorService
, backed by a pool of one or more threads. Give that task a Runnable
object whose run
method performs your desired task. Specify an initial delay, or zero to start immediately. And specify a period, an amount of time to pass before performing the task again.
Important: Be sure to eventually shutdown your executor service, when you no longer need it, or when your app is quitting. Otherwise the threads may continue running indefinitely.
Here is a Count
class to hold our count integer.
package work.basil.example;
public class Count
{
private int count;
public Count ( int count )
{
this.count = count;
}
public int getCount ( ) { return this.count; }
}
And here is some example code. We define a runnable, configure an executor service, start it running, wait a while, then shutdown the executor service.
Count count = new Count( 42 );
Runnable runnable = ( ) -> {
int i = count.getCount();
System.out.println( "Count is currently: " + i + " at " + ZonedDateTime.now() );
};
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
long delay = 0;
long period = 5;
ses.scheduleAtFixedRate( runnable , delay , period , TimeUnit.SECONDS );
// … eventually shutdown your executor service and its backing thread pool.
try
{
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
ses.shutdown();
System.out.println( "Done running example of scheduled executor service." );
When run.
Count is currently: 42 at 2020-04-25T13:08:14.484127-07:00[America/Los_Angeles]
Count is currently: 42 at 2020-04-25T13:08:19.465306-07:00[America/Los_Angeles]
…
Count is currently: 42 at 2020-04-25T13:09:14.460006-07:00[America/Los_Angeles]
Done running example of scheduled executor service.
Sharing resources across threads, in this case the Count
instance and its contained int
variable, carries complications. Study the superb book Java Concurrency in Practice by Brian Goetz, et al.
I imagine that you intend to increment that count during the execution of your app. If so, we must protect access to the count because one thread could be modifying the number while another is reading it.
Java provides the AtomicInteger
class for this purpose. Change the primitive int
in our Count
class to use AtomicInteger
object instead. We add an increment
method to increase the value of that AtomicInteger
. And we use the thread-safe AtomicInteger::get
method to retrieve its value.
package work.basil.example;
import java.util.concurrent.atomic.AtomicInteger;
public class Count
{
private AtomicInteger count;
public Count ( int count )
{
this.count = new AtomicInteger( count );
}
public int getCount ( ) { return this.count.get(); }
public int increment ( ) { return this.count.incrementAndGet(); }
}
We need not make any changes to our code that uses Count
class.
If you want to see the incrementing in action, set up another Runnable
task. Schedule that task on the executor service as well as our other Runnable
.
Count count = new Count( 42 );
Runnable runnable = ( ) -> {
int i = count.getCount();
System.out.println( "Count is currently: " + i + " at " + ZonedDateTime.now() );
};
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
long delay = 0;
long period = 5;
ses.scheduleAtFixedRate( runnable , delay , period , TimeUnit.SECONDS );
Runnable runnableIncrementor = ( ) -> {
int i = count.increment();
System.out.println( "Incrementing count: " + i + " at " + ZonedDateTime.now() );
};
ses.scheduleAtFixedRate( runnableIncrementor , 7 , 13 , TimeUnit.SECONDS );
// … eventually shutdown your executor service and its backing thread pool.
try
{
Thread.sleep( TimeUnit.MINUTES.toMillis( 1 ) );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
ses.shutdown();
System.out.println( "Done running example of scheduled executor service." );
When run.
Count is currently: 42 at 2020-04-25T13:26:14.405538-07:00[America/Los_Angeles]
Count is currently: 42 at 2020-04-25T13:26:19.372358-07:00[America/Los_Angeles]
Incrementing count: 43 at 2020-04-25T13:26:21.379639-07:00[America/Los_Angeles]
Count is currently: 43 at 2020-04-25T13:26:24.371442-07:00[America/Los_Angeles]
…