Multithreading using synchronized - java

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.

Related

AtomicInteger a1 increase before a2, and decrease after a2, why exists a2 > a1

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.

Thread.sleep() stopping all threads

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

What is the real time benefits of creating Thread in Java?

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 ...

Object Sharing in Simple Multi-threaded Program

Introduction
I have written a very simple program as an attempt to re-introduce myself to multi-threaded programming in JAVA. The objective of my program is derived from this rather neat set of articles, written by Jakob Jankov. For the program's original, unmodified version, consult the bottom of the linked article.
Jankov's program does not System.out.println the variables, so you cannot see what is happening. If you .print the resulting value you get the same results, every time (the program is thread safe); however, if you print some of the inner workings, the "inner behaviour" is different, each time.
I understand the issues involved in thread scheduling and the unpredictability of a thread's Running. I believe that may be a factor in the question I ask, below.
Program's Three Parts
The Main Class:
public class multiThreadTester {
public static void main (String[] args) {
// Counter object to be shared between two threads:
Counter counter = new Counter();
// Instantiation of Threads:
Thread counterThread1 = new Thread(new CounterThread(counter), "counterThread1");
Thread counterThread2 = new Thread(new CounterThread(counter), "counterThread2");
counterThread1.start();
counterThread2.start();
}
}
The objective of the above class is simply to share an object. In this case, the threads share an object of type Counter:
Counter Class
public class Counter {
long count = 0;
// Adding a value to count data member:
public synchronized void add (long value) {
this.count += value;
}
public synchronized long getValue() {
return count;
}
}
The above is simply the definition of the Counter class, which includes only a primitive member of type long.
CounterThread Class
Below, is the CounterThread class, virtually unmodified from the code provided by Jankov. The only real difference (despite my implementing Runnable as opposed to extending Thread) is the addition of System.out.println(). I added this to watch the inner-workings of the program.
public class CounterThread implements Runnable {
protected Counter counter = null;
public CounterThread(Counter aCounter) {
this.counter = aCounter;
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("BEFORE add - " + Thread.currentThread().getName() + ": " + this.counter.getValue());
counter.add(i);
System.out.println("AFTER add - " + Thread.currentThread().getName() + ": " + this.counter.getValue());
}
}
}
Question
As you can see, the code is very simple. The above code's only purpose is to watch what happens as two threads share a thread-safe object.
My question comes as a result of the output of the program (which I have tried to condense, below). The output is hard to "get consistent" to demonstrate my question, as the spread of the difference (see below) can be quite great:
Here's the condensed output (trying to minimize what you look at):
AFTER add - counterThread1: 0
BEFORE add - counterThread1: 0
AFTER add - counterThread1: 1
BEFORE add - counterThread1: 1
AFTER add - counterThread1: 3
BEFORE add - counterThread1: 3
AFTER add - counterThread1: 6
BEFORE add - counterThread1: 6
AFTER add - counterThread1: 10
BEFORE add - counterThread2: 0 // This BEFORE add statement is the source of my question
And one more output that better demonstrates:
BEFORE add - counterThread1: 0
AFTER add - counterThread1: 0
BEFORE add - counterThread1: 0
AFTER add - counterThread1: 1
BEFORE add - counterThread2: 0
AFTER add - counterThread2: 1
BEFORE add - counterThread2: 1
AFTER add - counterThread2: 2
BEFORE add - counterThread2: 2
AFTER add - counterThread2: 4
BEFORE add - counterThread2: 4
AFTER add - counterThread2: 7
BEFORE add - counterThread2: 7
AFTER add - counterThread2: 11
BEFORE add - counterThread1: 1 // Here, counterThread1 still believes the value of Counter's counter is 1
AFTER add - counterThread1: 13
BEFORE add - counterThread1: 13
AFTER add - counterThread1: 16
BEFORE add - counterThread1: 16
AFTER add - counterThread1: 20
My question(s):
Thread safety ensures the safe mutability of a variable, i.e. only one thread can access an object at a time. Doing this ensures that the "read" and "write" methods will behave, appropriately, only writing after a thread has released its lock (eliminating racing).
Why, despite the correct write behaviour, does counterThread2 "believe" Counter's value (not the iterator i) to still be zero? What is happening in memory? Is this a matter of the thread containing it's own, local Counter object?
Or, more simply, after counterThread1 has updated the value, why does counterThread2 not see - in this case, System.out.println() - the correct value? Despite not seeing the value, the correct value is written to the object.
Why, despite the correct write behaviour, does counterThread2 "believe" Counter's value to still be zero?
The threads interleaved in such a way to cause this behaviour. Because the print statements are outside of the synchronised block, it is possible for a thread to read the counter value then pause due to is scheduling while the other thread increments multiple times. When the waiting thread finally resumes and enters the inc counter method, the value of the counter will have moved on quite a bit and will no longer match what was printed in the BEFORE log line.
As an example, I have modified your code to make it more evident that both threads are working on the same counter. First I have moved the print statements into the counter, then I added a unique thread label so that we can tell which thread was responsible for the increment and finally I only increment by one so that any jumps in the counter value will stand out more clearly.
public class Main {
public static void main (String[] args) {
// Counter object to be shared between two threads:
Counter counter = new Counter();
// Instantiation of Threads:
Thread counterThread1 = new Thread(new CounterThread("A",counter), "counterThread1");
Thread counterThread2 = new Thread(new CounterThread("B",counter), "counterThread2");
counterThread1.start();
counterThread2.start();
}
}
class Counter {
long count = 0;
// Adding a value to count data member:
public synchronized void add (String label, long value) {
System.out.println(label+ " BEFORE add - " + Thread.currentThread().getName() + ": " + this.count);
this.count += value;
System.out.println(label+ " AFTER add - " + Thread.currentThread().getName() + ": " + this.count);
}
public synchronized long getValue() {
return count;
}
}
class CounterThread implements Runnable {
private String label;
protected Counter counter = null;
public CounterThread(String label, Counter aCounter) {
this.label = label;
this.counter = aCounter;
}
public void run() {
for (int i = 0; i < 10; i++) {
counter.add(label, 1);
}
}
}

Synchronization of many objects and counter

I know it lot of people have problems with this topic and you might be bored, but I try to understand it since few days and still don't know how it works:(. I have a counter object, and other objects of another class (in the future more then one class). Now each object should respond for counters execution. One count - one step of each objects run method. That's my code:
public class Th {
private final static Object lock1 = new Object();
////////////////////////////////////////////////////////////////////////////////
private class Stop implements Runnable {
private int count, id;
public Stop(int id) {
this.count = 0;
this.id = id;
}
#Override public void run() {
synchronized(lock1){
while (count < 20) {
try {
lock1.wait();
}
catch (InterruptedException exception) {
System.out.println("Error!");
}
System.out.println(count + " stop " + id);
this.count++;
// try {
// Thread.sleep(360);
// }
// catch (InterruptedException exception) {
// System.out.println("Error!");
// }
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
private class Counter implements Runnable {
private int count;
public Counter() {
this.count = 0;
}
#Override public void run() {
synchronized(lock1){
while (count<15) {
lock1.notifyAll();
System.out.println(count + " counter");
this.count++;
// try {
// Thread.sleep(360);
// }
// catch (InterruptedException exception) {
// System.out.println("Error!");
// }
}
}
}
}
public void test() {
Stop s1 = new Stop(1);
Stop s2 = new Stop(2);
Stop s3 = new Stop(3);
Counter counter = new Counter();
(new Thread(s1)).start();
(new Thread(s2)).start();
(new Thread(counter)).start();
(new Thread(s3)).start();
}
}
and it returns me something like:
run:
0 counter
1 counter
2 counter
3 counter
4 counter
5 counter
6 counter
7 counter
8 counter
9 counter
10 counter
11 counter
12 counter
13 counter
14 counter
0 stop 1
what I need is:
0 counter
0 stop 0
0 stop 1
0 stop 2
1 counter
1 stop 0
1 stop 1
1 stop 2
2 counter
2 stop 0
2 stop 1
2 stop 2
3 counter
...
The entire loop of the Counter thread is synchronizd on lock1. This means that although you call notifyAll in this loop, other threads can't reacquire the lock until the complete loop has ended in the Counter thread.
Make each iteration of the loop synchronized, instead of synchronizing outside of the loop.
Note that this won't be sufficient, though, because the Counter thread might reacquire the lock before all the Stop threads have reacquired it. You'll need to make the Counter thread wait on another condition, and make it restart when all the Stop threads have displayed the count.
You should investigate higher-level abstractions, like CyclicBarrier and CountDownLatch.
First, the best way to solve this is to use the higher level synchronization classes, as JB Nizet says.
But if you want to do it "the hard way" as a learning exercise, you need to recognize that this problem requires the Counter and Stop threads to wait for specific "events".
The Stop threads need to wait until the Counter thread tells them to advance to the next stop.
The Counter thread needs to wait until all Stop threads have advanced and stopped.
One problem with your current implementation is that the Counter thread has nothing to tell it when all of the Stop threads have advanced and stopped. Instead, it assumes that when it sees a notify event that it is ok to issue the next count.

Categories