class ThreadDemo extends Thread
{
public void run()
{
for(int i =0; i<5;i++)
{
System.out.println(i);
}
}
}
class ThreadApp
{
public static void main(String args[])
{
ThreadDemo thread1 = new ThreadDemo();
thread1.start();
ThreadDemo thread2 = new ThreadDemo();
thread2.start();
ThreadDemo thread3 = new ThreadDemo();
thread3.start();
}
}
Output:
0
2
3
1
4
1
2
4
3
0
0
1
2
3
4
By default, Java applications are single thread application. We are going for the concept called multithreading to share the work. Means, instead of doing the work with one thread (Main thread) if we create the thread, then it will simplifies the work. I understood this thing theoretically. My doubt arises when I start coding.
In the above program I have created 3 threads. If 3 threads are working on the same logic (iteration and printing the values by using for loop) why it is giving 3 separate output instead of giving one set of values from 0 to 4?
Duplicating the work, not sharing the work
You said:
We are going for the concept called multithreading to share the work.
But you did not share the work. You repeated the work. Instead of running a for loop once, you ran a for loop three times, once in each of three threads.
You asked:
why it is giving 3 separate output instead of giving one set of values from 0 to 4?
If a school teacher asks each of three pupils to write the alphabet on the board, we would end up not with 26 letters but with 78 letters (3 * 26). Each pupil would be looping through the letters of the alphabet. Likewise, each of your three threads looped through the count of zero to four.
Your for loop is local to (within) your task code. So each thread runs all of that code starting at the top. So the for loop executes three times, one per thread.
Beware: System.out prints out-of-order
Sending text to System.out via calls to println or print does not result in text appearing immediately and in the order sent. The lines of text you send may appear out of order.
When examining a sequence of statements, always include a timestamp such as java.time.Instant.now(). Then study the output. You may need to manually re-order the outputs using a text editor to see the true sequence.
You can see the out-of-chronological-order lines in my own example output below.
Executor service
In modern Java we no longer need address the Thread class directly. Generally best to use the Executors framework. See Oracle Tutorial.
package work.basil.example;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Counter
{
public static void main ( String[] args )
{
ExecutorService executorService = Executors.newCachedThreadPool();
Runnable task = new Runnable()
{
#Override
public void run ( )
{
for ( int i = 0 ; i < 5 ; i++ )
{
System.out.println( "i = " + i + " at " + Instant.now() + " on thread " + Thread.currentThread().getName() );
}
}
};
executorService.submit( task );
executorService.submit( task );
executorService.submit( task );
// Let our program run a while, then gracefully shutdown the executor service.
// Otherwise the backing thread pool may run indefinitely, like a zombie 🧟‍.
try { Thread.sleep( Duration.ofSeconds( 5 ).toMillis() ); }catch ( InterruptedException e ) { e.printStackTrace(); }
executorService.shutdown();
try { executorService.awaitTermination( 1 , TimeUnit.MINUTES ); } catch ( InterruptedException e ) { e.printStackTrace(); }
}
}
When run.
i = 0 at 2021-01-06T05:28:34.349290Z on thread pool-1-thread-1
i = 1 at 2021-01-06T05:28:34.391997Z on thread pool-1-thread-1
i = 0 at 2021-01-06T05:28:34.349246Z on thread pool-1-thread-2
i = 0 at 2021-01-06T05:28:34.349464Z on thread pool-1-thread-3
i = 1 at 2021-01-06T05:28:34.392467Z on thread pool-1-thread-3
i = 2 at 2021-01-06T05:28:34.392162Z on thread pool-1-thread-1
i = 2 at 2021-01-06T05:28:34.392578Z on thread pool-1-thread-3
i = 3 at 2021-01-06T05:28:34.392670Z on thread pool-1-thread-1
i = 3 at 2021-01-06T05:28:34.392773Z on thread pool-1-thread-3
i = 4 at 2021-01-06T05:28:34.393165Z on thread pool-1-thread-3
i = 1 at 2021-01-06T05:28:34.392734Z on thread pool-1-thread-2
i = 4 at 2021-01-06T05:28:34.392971Z on thread pool-1-thread-1
i = 2 at 2021-01-06T05:28:34.395138Z on thread pool-1-thread-2
i = 3 at 2021-01-06T05:28:34.396407Z on thread pool-1-thread-2
i = 4 at 2021-01-06T05:28:34.397002Z on thread pool-1-thread-2
Project Loom
Project Loom promises to be bring to Java new features such as virtual threads (fibers), and making ExecutorService be AutoCloseable for use with try-with-resources to automatically shutdown.
Let's rewrite the above code to use Project Loom technologies. Preliminary builds are available now based on early-access Java 16.
Also, we can rewrite the anonymous class seen above with simpler lambda syntax.
Another difference from above: Virtual threads do not have a name. So we switch to using the id number of the thread to differentiate between threads running.
try
(
ExecutorService executorService = Executors.newVirtualThreadExecutor() ;
)
{
Runnable task = ( ) -> {
for ( int i = 0 ; i < 5 ; i++ )
{
System.out.println( "i = " + i + " at " + Instant.now() + " on thread " + Thread.currentThread().getId() );
}
};
executorService.submit( task );
executorService.submit( task );
executorService.submit( task );
}
// At this point, the flow-of-control blocks until all submitted tasks are done.
// And the executor service is also shutdown by this point.
When run.
i = 0 at 2021-01-06T05:41:36.628800Z on thread 17
i = 1 at 2021-01-06T05:41:36.647428Z on thread 17
i = 2 at 2021-01-06T05:41:36.647626Z on thread 17
i = 3 at 2021-01-06T05:41:36.647828Z on thread 17
i = 4 at 2021-01-06T05:41:36.647902Z on thread 17
i = 0 at 2021-01-06T05:41:36.628842Z on thread 14
i = 1 at 2021-01-06T05:41:36.648148Z on thread 14
i = 2 at 2021-01-06T05:41:36.648227Z on thread 14
i = 3 at 2021-01-06T05:41:36.648294Z on thread 14
i = 4 at 2021-01-06T05:41:36.648365Z on thread 14
i = 0 at 2021-01-06T05:41:36.628837Z on thread 16
i = 1 at 2021-01-06T05:41:36.648839Z on thread 16
i = 2 at 2021-01-06T05:41:36.648919Z on thread 16
i = 3 at 2021-01-06T05:41:36.648991Z on thread 16
i = 4 at 2021-01-06T05:41:36.649054Z on thread 16
Sharing state across threads
If you really wanted to share values across the threads, you define them outside the immediate task code.
In this next example, we define a class Counter that implements Runnable. As a Runnable we can pass an instance of this class to an executor service. We defined a member field, a ConcurrentMap (a thread-safe Map) that tracks each of our desired numbers 0-4. For each of those five numbers, we map to the id number of the virtual thread that was able to beat the other virtual threads to submitting that entry into our originally-empty map.
Be aware that we are submitting a single Counter object to all three threads. So all three threads have access to the very same ConcurrentMap object. That is why we must use a ConcurrentMap rather than a plain Map. Any resource shared across threads must be built to be thread-safe.
We are calling Thread.sleep to try to mix things up. Otherwise, the first thread might finish all the work while the main thread is still submitting to the second and third threads.
package work.basil.example;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.*;
public class Counter implements Runnable
{
public ConcurrentMap < Integer, Long > results = new ConcurrentHashMap <>();
#Override
public void run ( )
{
try { Thread.sleep( Duration.ofMillis( 100 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
Long threadId = Thread.currentThread().getId(); // ID of this thread.
for ( int i = 0 ; i < 5 ; i++ )
{
// Shake things up by waiting some random time.
try { Thread.sleep( Duration.ofMillis( ThreadLocalRandom.current().nextInt(1, 100) ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
results.putIfAbsent( i , threadId ); // Auto-boxing converts the `int` value of `i` to be wrapped as a `Integer` object.
}
}
}
Here is a main method to make our exercise happen.
public static void main ( String[] args )
{
Counter counter = new Counter();
try
(
ExecutorService executorService = Executors.newVirtualThreadExecutor() ;
)
{
executorService.submit( counter );
executorService.submit( counter );
executorService.submit( counter );
}
// At this point, the flow-of-control blocks until all submitted tasks are done.
// And the executor service is also shutdown by this point.
System.out.println( "counter.results = " + counter.results );
}
In the results of this particular run, we can see that the two threads number 16 and 17 had all the success in putting entries into our map. The third thread was not able to be the first to put in any of the five entries.
counter.results = {0=16, 1=17, 2=17, 3=16, 4=16}
Try to do some various tests and see by yourself what is coming and from where
public class ThreadApp {
public static void main(String args[]) throws InterruptedException {
ThreadDemo thread1 = new ThreadApp().new ThreadDemo("t1",4);
ThreadDemo thread2 = new ThreadApp().new ThreadDemo("t2",7);
thread2.start();
thread1.start();
ThreadDemo thread3 = new ThreadApp().new ThreadDemo("t3",2);
// wait till t1 &t2 finish run then launch t3
thread1.join();
thread2.join();
thread3.start();
}
class ThreadDemo extends Thread {
int stop;
public ThreadDemo(String name, int stop) {
super(name);
this.stop = stop;
}
public void run() {
for (int i = 0; i < stop; i++) {
System.out.println(this.getName() + ":" + i);
}
}
}
}
Possible Output:
t2:0
t2:1
t1:0
t2:2
t1:1
t2:3
t1:2
t2:4
t1:3
t2:5
t2:6
//due to join t3 start only after t1 & t2 finish their run
t3:0
t3:1
Related to benefits, just one hint Producer-Consumer problem ...
Related
I have created a simple class in Java with a method that invoke 2 thread (each thread has a simple loop with a println and a pause in each iteration).
In the method "mymethod" I've created and started my two threads.
Between Threads invocation, I've placed two prints. Print occur before threads end, and I assume that the order is as follows:
first print
first thread start
second thread start
second print
third print
Why the last instruction is executed only when all threads finish their computation instead to return 0 (last instruction)?
Obviously I know that this makes sense, but the return is an instruction like all others? Is the JVM that preventing the return if not all threads are finished yet?
public class Main {
public int mymethod (){
System.out.println("start");
Th t1 = new Th(); //this is a my thread instance
t1.start();
System.out.println("started th1");
Th t2 = new Th();
t2.start();
System.out.println("started th2");
System.out.println("Finished");
return 0;
}
public static void main(String[] args) {
// write your code here
Main m = new Main();
m.mymethod();
}
}
This is the output:
start
started th1
started th2
Finished
0
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
Process finished with exit code 0
there is the code:
AtomicInteger a1 = new AtomicInteger();
AtomicInteger a2 = new AtomicInteger();
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 1e4; j++) {
a1.incrementAndGet();
a2.incrementAndGet();
int v2 = a2.decrementAndGet();
if(v2>a1.get()){
System.out.println("error a2 > a1");
}
a1.decrementAndGet();
}
}).start();
}
why exists println error a2 > a1?
thanks!
action
a1
a2
a1+
1
0
a2+
1
1
a2-
1
0
a1-
0
0
use redhat windows openjdk-1.8.0.222
Imagine this scenario:
All 100 threads complete the increments and the last thread now has a1 = 100, a2 = 100.
Now the first thread to calculate v2 will get v2 = 99.
If the other 99 threads finish and decrement before that one thread goes any further, it will then be checking 99 > 1 and be true.
Short answer: Other threads can decrement a1 between int v2 = a2.decrementAndGet() and if(v2>a1.get())
The Answer by jdkramhoft is correct. You have two resources (the pair of AtomicInteger objects) being manipulated across threads that are not protected. Each individual call to increment, decrement, and get on each AtomicInteger happens atomically. But multiple calls across threads may be interleaved, not atomic, not thread-safe.
To make the group of increment, decrement, and get calls atomic, you must guard them as a group. One easy way to do that is to use synchronized. In the code below we arbitrarily chose our AtomicInteger named a1 to be the lock object for our synchronized call.
By synchronizing the code block on the same object (a1), we guarantee that only one thread at a time may be running that code block. When the other thread gets to the point of trying to run that block, it must wait for the synchronized lock to be freed.
The use of 1e4 is a bit precious, so let's use 10_000 instead. Even 1_000 is enough for our purposes here, and is less likely to blow out our console buffer.
I am no expert on concurrency, but the following code seems thread-safe to me. Perhaps others can point out any flaws. At any rate, use at your own risk.
package work.basil.demo.threads;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
public class App
{
public static void main ( String[] args )
{
System.out.println( "INFO - Starting the main method of our demo. " + Instant.now() );
AtomicInteger a1 = new AtomicInteger();
AtomicInteger a2 = new AtomicInteger();
for ( int i = 0 ; i < 100 ; i++ )
{
System.out.println( "Instantiating thread # " + i + " | " + Instant.now() );
new Thread( ( ) -> {
for ( int j = 0 ; j < 1_000 ; j++ )
{
synchronized ( a1 )
{
a1.incrementAndGet();
a2.incrementAndGet();
int v2 = a2.decrementAndGet();
if ( v2 > a1.get() )
{
System.out.println( "error a2 > a1" );
}
a1.decrementAndGet();
}
System.out.println( "Finishing thread id " + Thread.currentThread().getId() + " | " + Instant.now() );
}
} ).start();
}
try { Thread.sleep( Duration.ofSeconds( 10 ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "a1 = " + a1.get() );
System.out.println( "a2 = " + a2.get() );
}
}
When run.
INFO - Starting the main method of our demo. 2021-06-06T00:05:12.869874Z
Instantiating thread # 0 | 2021-06-06T00:05:12.875289Z
Instantiating thread # 1 | 2021-06-06T00:05:12.887569Z
Instantiating thread # 2 | 2021-06-06T00:05:12.888805Z
Instantiating thread # 3 | 2021-06-06T00:05:12.891298Z
Finishing thread id 16 | 2021-06-06T00:05:12.891789Z
Finishing thread id 16 | 2021-06-06T00:05:12.894783Z
…
Finishing thread id 57 | 2021-06-06T00:05:13.634241Z
Finishing thread id 57 | 2021-06-06T00:05:13.634245Z
Finishing thread id 57 | 2021-06-06T00:05:13.634247Z
a1 = 0
a2 = 0
By the way, a quick mention: In modern Java, we rarely need to address the Thread class directly. Better to use the Executors framework added to Java 5. Submit your tasks as Runnable/Callable object to be run on background threads.
I am learning multithreading. I have a following code:
public class Intro {
public static void main(String[] args) {
new Intro().doCounter();
}
private int counter = 0;
synchronized void increment() {
counter++;
}
private void doCounter() {
Thread thread1 = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
increment();
}
System.out.println("first: " + counter);
}
});
Thread thread2 = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
increment();
}
System.out.println("second: " + counter);
}
});
thread1.start();
thread2.start();
}
}
Output (can differ):
first: 1741739
second: 2000000
The code has two threads. One thread increment the counter million times, while the second wait. Then the second increment it million times as well. I understand why the second thread get 2 million, but didn't get why the first thread get 1741739. Why it isn't 1 million for the first thread? I think it had to stop at 1 million. Thanks for explanation.
As discussed in the comments, you have two problems.
You need to understand that threading execution is not predictable. Each thread gets scheduled for a certain amount of time on a CPU core as decided by the JVM and the OS. How often each thread gets scheduled, and for how long, depends on conditions at runtime. So their work can be interleaved, with no guarantee as to which thread will run when, or finish first/last.
Your code is accessing a resource, a member field variable counter across threads without protection. This is not thread-safe. Due to modern CPU architecture and the Java Memory Model, your two threads might see two different states of that single variable. For example, each of two cores running your two threads might each have a different copy of the variable in its cache.
One solution is to use AtomicInteger as your counter variable rather than int. An AtomicInteger is thread-safe, protecting access to its contained int value. By using AtomicInteger, you no longer need your synchronized increment method.
Here is some example code for that.
An Incrementor class that contains our AtomicInteger variable named count as a member field. The class carries nested class IncrementingTask that is a Runnable with a run method to be executed on background threads, incrementing our count var a million times. The class has a demo method for starting any number of threads, each thread running an instance of IncrementingTask to increment the million times.
package work.basil.threading;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Incrementor
{
// Member fields.
final private AtomicInteger count = new AtomicInteger( 0 );
public void demo ( )
{
int threadsLimit = 2;
ExecutorService executorService = Executors.newFixedThreadPool( threadsLimit );
for ( int i = 0 ; i < threadsLimit ; i++ )
{
executorService.submit( new IncrementingTask() );
}
executorService.shutdown();
try { executorService.awaitTermination( 1 , TimeUnit.HOURS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
// At this point, the tasks are all done/canceled/failed.
System.out.println( "RESULTS: count is at: " + this.count.get() );
}
// Runnable task.
class IncrementingTask implements Runnable
{
#Override
public void run ( )
{
for ( int i = 0 ; i < 1_000_000 ; i++ )
{
int incrementedCount = count.incrementAndGet();
if ( i % 100_000 == 0 )
{
System.out.println( "Thread # " + Thread.currentThread().getId() + " incremented count to: " + incrementedCount + " at " + Instant.now() );
}
}
System.out.println( "Thread # " + Thread.currentThread().getId() + " is done incrementing. " + Instant.now() );
}
}
}
A class to run this.
package work.basil.threading;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
Incrementor incrementor = new Incrementor();
incrementor.demo();
}
}
When run.
Thread # 15 incremented count to: 2 at 2021-03-29T00:04:37.099350Z
Thread # 14 incremented count to: 1 at 2021-03-29T00:04:37.099281Z
Thread # 14 incremented count to: 200002 at 2021-03-29T00:04:37.155680Z
Thread # 15 incremented count to: 198267 at 2021-03-29T00:04:37.155659Z
Thread # 15 incremented count to: 387108 at 2021-03-29T00:04:37.165850Z
Thread # 14 incremented count to: 400002 at 2021-03-29T00:04:37.165894Z
Thread # 15 incremented count to: 552150 at 2021-03-29T00:04:37.168378Z
Thread # 14 incremented count to: 631000 at 2021-03-29T00:04:37.169329Z
Thread # 15 incremented count to: 753457 at 2021-03-29T00:04:37.170931Z
Thread # 14 incremented count to: 840455 at 2021-03-29T00:04:37.171943Z
Thread # 15 incremented count to: 942263 at 2021-03-29T00:04:37.173276Z
Thread # 14 incremented count to: 1049089 at 2021-03-29T00:04:37.174559Z
Thread # 15 incremented count to: 1135900 at 2021-03-29T00:04:37.175726Z
Thread # 14 incremented count to: 1240800 at 2021-03-29T00:04:37.177092Z
Thread # 15 incremented count to: 1308191 at 2021-03-29T00:04:37.177678Z
Thread # 15 incremented count to: 1439696 at 2021-03-29T00:04:37.179523Z
Thread # 15 incremented count to: 1595283 at 2021-03-29T00:04:37.180795Z
Thread # 14 incremented count to: 1604140 at 2021-03-29T00:04:37.181720Z
Thread # 14 incremented count to: 1800001 at 2021-03-29T00:04:37.183231Z
Thread # 14 incremented count to: 1900001 at 2021-03-29T00:04:37.184697Z
Thread # 15 is done incrementing. 2021-03-29T00:04:37.182735Z
Thread # 14 is done incrementing. 2021-03-29T00:04:37.188265Z
RESULTS: count is at: 2000000
Notice that every time we run this, we always get to exactly 2,000,000 million total. Also notice that every time you run this, the list of which thread ran when will vary, as will the count at each time the thread reports to us on the console.
Console lies
Notice the counts look crazy. The first two lines report a count of 2 and then 1 rather than one then two. Same with the third and fourth lines, apparently backwards.
Look more closely to examine the microseconds of the timestamps. Those timestamps are not in chronological order.
Lesson learned: The System.out.println calls do not present on the console in chronological order. So never rely on such console output to give you a true picture of what happened when.
Always include a timestamp such as Instant.now() or System.nanoTime(). If you want to see messages in order, collect them in a thread-safe ordered collection such as Collections.synchronizedList​( new ArrayList< String >() ).
Alternatively, if you wanted to keep your int variable and synchronized increment method rather than use AtomicInteger you must protect access to the counter variable in a thread-safe manner. Marking the variable as volatile would help. You can search Stack Overflow to learn more on this.
As per my understanding if there are multiple threads executing in parallel, Thread.sleep() will only sleep the thread in which it is called. However in my case Thread.sleep is making other threads sleep/wait. Is this expected behavior or am i doing something incorrect.
Find below my code
package stackoverflow;
public class SO {
public static void main(String[] args){
CallMe callMe = new CallMe();
Producer producer = new Producer(callMe);
Consumer consumer = new Consumer(callMe);
producer.thread.start();
consumer.thread.start();
}
}
class CallMe{
int a;
synchronized public int get(){
System.out.println("Get "+a);
return a;
}
synchronized public void put(int a){
System.out.println("Put "+a);
this.a=a;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Producer implements Runnable{
Thread thread;
CallMe callMe;
Producer(CallMe callMe){
thread=new Thread(this);
this.callMe=callMe;
}
public void run(){
int i=0;
while(true){
callMe.put(i);
i++;
}
}
}
class Consumer implements Runnable{
Thread thread;
CallMe callMe;
Consumer(CallMe callMe){
thread = new Thread(this);
this.callMe=callMe;
}
public void run(){
while (true) {
callMe.get();
}
}
}
While the put thread is put to sleep, the get should keep printing the output right? I am not giving any priority to any of the threads so if one thread is sleeping other should start executing. Isn't it?
I tried giving large value of sleep (100000000 millis) still console only shows Put 0 not printing Get <X> at all.
You have synchronized on both methods of your CallMe class. There is only one lock on which to synchronize for each object of that class. And you produce a singleton of that class. So one object with one lock being called via either of two methods across two threads.
Within one of these methods you sleep. During that sleep, you are holding the lock. The consumer thread calls the get method while the producer is holding the lock. The get method blocks, waiting for the lock, stopping the thread of the consumer. Eventually the producer thread wakes, releases the lock. The consumer thread grabs the lock, and proceeds.
This behavior is shown on the tutorial by Oracle.
Be sure to study the classic book by Brian Goetz, et al., Java Concurrency in Practice.
You have another issue as well.
You are sharing a resource, the int member field of CallMe, across two threads. Your code there is subject to visibility problems per the Java Memory Model. Because of modern CPU architecture, two threads accessing the same primitive value or object reference may see two different cached values. One solution is use of volatile keyword. I prefer the alternative, using the Atomic… classes.
I would replace that that with a final member field of type AtomicInteger.
Using AtomicInteger would also allow you to eliminate the synchronized tags, thereby eliminating your frozen-thread bottleneck.
So, killing two birds with one stone, we solve both your contention problem and your visibility problem by using AtomicInteger.
I suggest you learn about using executor service rather than addressing the Thread class directly.
To run a task repeatedly, use a scheduled executor service rather than sleeping.
Putting all that advice together, we might get code that looks like this.
package work.basil.example.prodcon;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
public class CountManager
{
final AtomicInteger counter;
// Constructor
public CountManager ( )
{
this.counter = new AtomicInteger();
System.out.println( "CountManager constructor initialized the count to = " + this.counter.get() + " at " + Instant.now() );
}
public int getCount ( )
{
return this.counter.get();
}
public int increment ( )
{
return this.counter.incrementAndGet();
}
}
package work.basil.example.prodcon;
import java.time.Instant;
public class Producer implements Runnable
{
final private CountManager countManager;
public Producer ( CountManager countManager )
{
this.countManager = countManager;
}
#Override
public void run ( )
{
int newCount = this.countManager.increment();
System.out.println( "The producer in thread # " + Thread.currentThread().getId()+ " incremented newCount = " + newCount + " at " + Instant.now() );
}
}
package work.basil.example.prodcon;
import java.time.Instant;
public class Consumer implements Runnable
{
final private CountManager countManager;
public Consumer ( CountManager countManager )
{
this.countManager = countManager;
}
#Override
public void run ( )
{
int currentCount = countManager.getCount();
System.out.println( "The consumer on thread # " + Thread.currentThread().getId() + " reports currentCount = " + currentCount + " at " + Instant.now() );
}
}
Run that code in an app.
package work.basil.example.prodcon;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
System.out.println( "The app began demo on thread # " + Thread.currentThread().getId() + " at " + Instant.now() );
// Setup.
CountManager countManager = new CountManager();
Producer producer = new Producer( countManager );
Consumer consumer = new Consumer( countManager );
// Schedule some work to be done on background threads.
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate( producer , 0 , 7 , TimeUnit.SECONDS );
ses.scheduleAtFixedRate( consumer , 0 , 3 , TimeUnit.SECONDS );
// Let the app run a while.
try { Thread.sleep( Duration.ofMinutes( 1 ).toMillis() ); } catch ( InterruptedException e ) {e.printStackTrace(); }
ses.shutdown();
System.out.println( "The app ended demo on thread # " + Thread.currentThread().getId() + " at " + Instant.now() );
}
}
When run:
The app began demo on thread # 1 at 2021-02-14T02:10:09.981741Z
CountManager constructor initialized the count to = 0 at 2021-02-14T02:10:10.003056Z
The producer in thread # 14 incremented newCount = 1 at 2021-02-14T02:10:10.014164Z
The consumer on thread # 14 reports currentCount = 1 at 2021-02-14T02:10:10.026975Z
The consumer on thread # 14 reports currentCount = 1 at 2021-02-14T02:10:13.019270Z
The consumer on thread # 14 reports currentCount = 1 at 2021-02-14T02:10:16.016694Z
The producer in thread # 14 incremented newCount = 2 at 2021-02-14T02:10:17.016730Z
The consumer on thread # 14 reports currentCount = 2 at 2021-02-14T02:10:19.017666Z
The consumer on thread # 14 reports currentCount = 2 at 2021-02-14T02:10:22.017923Z
The producer in thread # 14 incremented newCount = 3 at 2021-02-14T02:10:24.015972Z
The consumer on thread # 14 reports currentCount = 3 at 2021-02-14T02:10:25.018212Z
The consumer on thread # 14 reports currentCount = 3 at 2021-02-14T02:10:28.048834Z
The producer in thread # 14 incremented newCount = 4 at 2021-02-14T02:10:31.016746Z
The consumer on thread # 14 reports currentCount = 4 at 2021-02-14T02:10:31.016944Z
The consumer on thread # 14 reports currentCount = 4 at 2021-02-14T02:10:34.016674Z
The consumer on thread # 14 reports currentCount = 4 at 2021-02-14T02:10:37.017195Z
The producer in thread # 14 incremented newCount = 5 at 2021-02-14T02:10:38.016650Z
The consumer on thread # 14 reports currentCount = 5 at 2021-02-14T02:10:40.017862Z
The consumer on thread # 14 reports currentCount = 5 at 2021-02-14T02:10:43.016564Z
The producer in thread # 14 incremented newCount = 6 at 2021-02-14T02:10:45.017760Z
The consumer on thread # 14 reports currentCount = 6 at 2021-02-14T02:10:46.016909Z
The consumer on thread # 14 reports currentCount = 6 at 2021-02-14T02:10:49.014339Z
The producer in thread # 14 incremented newCount = 7 at 2021-02-14T02:10:52.013271Z
The consumer on thread # 14 reports currentCount = 7 at 2021-02-14T02:10:52.013389Z
The consumer on thread # 14 reports currentCount = 7 at 2021-02-14T02:10:55.017526Z
The consumer on thread # 14 reports currentCount = 7 at 2021-02-14T02:10:58.017050Z
The producer in thread # 14 incremented newCount = 8 at 2021-02-14T02:10:59.014334Z
The consumer on thread # 14 reports currentCount = 8 at 2021-02-14T02:11:01.013358Z
The consumer on thread # 14 reports currentCount = 8 at 2021-02-14T02:11:04.014889Z
The producer in thread # 14 incremented newCount = 9 at 2021-02-14T02:11:06.015598Z
The consumer on thread # 14 reports currentCount = 9 at 2021-02-14T02:11:07.014802Z
The consumer on thread # 14 reports currentCount = 9 at 2021-02-14T02:11:10.017313Z
The app ended demo on thread # 1 at 2021-02-14T02:11:10.017975Z
I have thread x which I start like so:
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(() -> {
Inside x I have a CyclicBarrier with another 10 threads:
final CyclicBarrier _threadGate = new CyclicBarrier(10);
ArrayList<Thread> _threadList = new ArrayList<>();
Then I add the thread to the list
for (int i = 0; i < 10; i++) {
_threadList.add(new Thread() {
#Override
public void run() {
_threadGate.await();
//long processing code
So after the threads are ready I start them, it is important for them to start at the same time (well almost, looping takes time, even if its 0,01ms):
for (int i = 0; i < _threadList.size(); i++) {
_threadList.get(i).start();
}
Now, the end of x, the main thread, is like this:
}, 0, repeatTimer, TimeUnit.SECONDS);
If repeatTimer is 300 this means that it starts again the 10 threads after 5 minutes.
The time for the 10 threads to finish is an UNKNOWN amount, but it is under 5 minutes. Somewhere between 2 and 4 minutes for sure.
What I want to achieve
Once the 10 threads finish, restart X but with a delay of 5 seconds.
For this I have been thinking of setting the repeatTimer value to the time elapsed by the 10 threads + 5 seconds (I dont know how to do it, I dont know w hen last thread finishes its task), but is this correct? or is there another way of doing it?
I don't see the necessity of having SchedulingExecutorService here. You can just wait until all threads finish their job using CountDownLatch.
Here's a simple exapmple:
while (!stopped) {
CountDownLatch latch = new CountDownLatch(N);
// create and start your threads
latch.await(); // this method blocks until each Thread calls countDown()
// wait here 5 seconds if you want
}
Decrement the latch in last action of each thread:
public void run() {
_threadGate.await();
// thread actions
latch.countDown();
}