Concurrency and Multithreading: The Synchronized Keyword Issues

Lesson: Ultimate Java Part 3: Concurrency and Multithreading -The Volatile Keyword video.

I use SDK Jetbrains Runtime Version 17.0.9 with the Language Level “17 - sealed types, always strict floating-point semantics” in IntelliJ Idea Community Edition.

ISSUES:

  1. The incrementTotalBytes() method in “The volatile keyword” lesson does not increment the TotalBytes() field in the “for loop” within the DownloadFileTask class which is invoked by thread1.
  2. The synchronized keyword does not synchronize access to the isDone field so that thread2 can run and exit successfully.

To recap: the totalBytes field is not updated, and thread2 does not exits.

I have re-watched numerous times all the videos in the Concurrency module and concluded that all my code is syntactically identical to Mosh’s code.

After careful analysis, I am left with these questions:

  1. When does thread1 send the interrupt indication for the code in the DownloadFileTask class to validate if (Thread.currentThread().isInterrupted()) return; in the overwritten run(); method?
  2. If isDone = true, when does while (!status.isDone()){} become false? There are no methods to change the value of isDone.

These aspects of the code in the lesson do not make sense to me. I cannot find a logical answer to these questions. I cannot help but think that there are lines of code missing that should address these questions within the lesson, and yet Mosh’s code works in the video (not in my IDE).

Here is my code for your review, I will include my code for the three classes (DownloadStatus, DownloadFileTask, and ThreadDemo) so that you can help me understand where the errors lie, please. I have renamed the DownloadStatus class to “VolatileKeyWordDemo”, otherwise my code is identical to the lessons’ code.:

  1. VolatileKeyWordDemo:
Summary
public class VolatileKeyWordDemo {


private boolean isDone;
private int totalBytes;
private int totalFiles;
private final Object totalBytesLock = new Object();
private  final Object totalFilesLock = new Object(); 


public void incrementTotalBytes(){
    synchronized (totalBytesLock){ 
        totalBytes++;}}
public  void incrementTotalFiles(){
    synchronized (totalFilesLock){ 
        totalFiles++;}}

public int getTotalBytes() {
    return totalBytes;}
public int getTotalFiles() {
    return totalFiles;}


public synchronized boolean isDone() {
   return isDone;
}
public synchronized void done() {
    isDone = true;
}

}// End of VolatileKeyWordDemo
  1. DownloadFileTask:
Summary
public class DownloadFileTask implements Runnable {

private VolatileKeyWordDemo status;
public DownloadFileTask(VolatileKeyWordDemo status) {
    this.status = new VolatileKeyWordDemo();}

    @Override
    public void run () {
        System.out.println("Downloading a File: " + Thread.currentThread().getName());
        for (var i = 0; i < 1_000_000; i++) {
            if (Thread.currentThread().isInterrupted()) return;
            status.incrementTotalBytes();}

        status.done();
        System.out.println();
        System.out.println("Download Complete: " + Thread.currentThread().getName() + " ");
    }


}// End of DownloadStatusDemo
  1. ThreadDemo:
Summary
public class ThreadDemo {

public static void show() {
    var status = new VolatileKeyWordDemo();
    var thread1 = new Thread(new DownloadFileTask(status));
    var thread2 = new Thread(() -> {
        System.out.println("Thread: " + Thread.currentThread().getName() +" has started.");
        while (!status.isDone()) {}
        System.out.println(status.getTotalBytes());}
    );
    thread1.start();
    thread2.start();}

public static void main(String[] args){
            show();}

}// End of ThreadDemo

This is the output of my program after I manually stop it:

Downloading a File: Thread-0
Thread: Thread-1 has started.

Download Complete: Thread-0

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

Any suggestions will be greatly appreciated.

I did this to his example and it worked. I changed the isDone to volatile and removed the synchronization from the two isDone methods.

var status = new DownloadStatus();
var thread1 = new Thread(new DownloadFileTask(status));
var thread2 = new Thread(new DownloadFileTask(status));

----- this didn’t work so I repeated thread 1 format.
// var thread2 = new Thread(()-> {
// while (!status.isDone()) {}
// System.out.println("Task done, total bytes: " + status.getTotalBytes());
// });
thread1.start();
thread2.start();
while (!status.isDone()) {}
System.out.println("Task done, total bytes: " + status.getTotalBytes());

My output looked like this:

Downloading a file: Thread-0
Downloading a file: Thread-1
Download complete: Thread-0
Download complete: Thread-1
Task done, total bytes: 2000000000

I have my loop counter set to 1 billion as 1 million didn’t do it for the race condition as my computer is too fast. Keep in mind he is using an older version of Java and some things work just a bit differently. I couldn’t get Locks to work.

Try it and play around with it, see if it works for you.
Doug

So, you ran thread2 as

var thread2 = new Thread(new DownloadFileTask(status));

Instead of :

 var thread2 = new Thread(()-> {
// while (!status.isDone()) {}
// System.out.println("Task done, total bytes: " + status.getTotalBytes());
// });

Did I understand correctly?

Yes. I couldn’t get his version to work

It appears that Mosh is using “JDK-12.01”, this is a screen-capture from the volatile keyword video.

Unfortunately, when I try to install “SDK-12.02”, I get the following error:

This User, on JetBrains’ Youtrack page, seems to have the same issue and the suggested answer does not work for me because I cannot choose the install folder except “Macintosh” when I installed it. Sucks. I was hoping to recreate Mosh’s environment.

The Synchronized keyword is still listed in Java 8 and Java 9. But it does not appear to produce the same results as you and I have both tried. There is lots of reading to be done here.

Does anyone else have any suggestions?

I downloaded the SDK 12 from here :point_right: the Java SE 12 Archive Downloads page from Oracle.

Update 1:
After many hurdles, I managed to install the JDK 12.01 that Mosh uses in the video.
Here are some screen-captures of IntelliJ using the same JDK:

However, this did not produce the same results for me as for Mosh, and the issue continues. This is disappointing.

I have lost count of how many times I have re-watched the LOCKS, SYNCHRONIZED, and VOLATILE keywords videos to review syntax. The Syntax I use is identical to Mosh and I am using the same JDK that he uses in his video as capture below:

I also tried using JDK 12.02, with no success. I will keep trying in hopes of resolving this problem.

I wonder if Mr. @Mosh could look into this? I reinstate my suspicions and questions below:

  1. isDone always remains true, so when does while (!status.isDone()){} become true? There are no methods to change the value of isDone from true to false so that while (!status.isDone()){} can be satisfied.

  2. When does thread1 send the interrupt indication for the code in the DownloadFileTask class to validate if (Thread.currentThread().isInterrupted()) return; in the overwritten run(); method? the code is missing the Thread.interrupted();

I cannot help but think that lines of code do not show on the video that could address these questions, and yet Mosh’s code works in the video (not in my IDE).

I managed to install the JDK 12.01 that Mosh uses in the video. However, this did not produce the same results for me as for Mosh.

JDK 12.02 was released on October 15, 2019. So he was probably using a 2019 MacBook Pro i7 with macOS Mojave (Liberty), per the release dates of both the hardware and the macOS. And I am using a 2020 MacBook Pro 13” i7. Both hardware allow multithreading at the hardware and software level.

Java shows this in the Activity Monitor before I run the ThreadDemo class and while it is running.