Monday, April 17, 2023

What is JavaOne?

JavaOne, Oracle Java, Oracle Java Career, Oracle Java Skill, Oracle Java Job, Oracle Java Prep, Oracle Java Preparation, Oracle Java Tutorial and Materials

JavaOne is a technology conference held annually by Oracle Corporation for Java developers and enthusiasts. The conference provides an opportunity for Java developers to come together and learn about the latest Java technologies, tools, and best practices. In this article, we will explore the history, purpose, and highlights of JavaOne.

History of JavaOne


JavaOne started in 1996 as the Java Developer Conference (JDC), an independent event organized by JavaSoft, a subsidiary of Sun Microsystems. The event was renamed JavaOne in 1997 when Sun Microsystems took over the organization. JavaOne quickly became the premier event for Java developers, attracting thousands of attendees every year.

In 2010, Oracle Corporation acquired Sun Microsystems, and JavaOne became an Oracle-sponsored event. Oracle has continued to organize JavaOne since then, although the conference has undergone several changes over the years.

Purpose of JavaOne


The purpose of JavaOne is to provide a forum for Java developers to learn, network, and share their experiences. The conference offers a variety of technical sessions, hands-on labs, and keynote speeches on topics such as Java development tools, cloud computing, and mobile development. Attendees can also meet with Java experts and get their questions answered.

JavaOne is also a platform for Oracle to showcase its Java-related products and services. The conference provides an opportunity for Oracle to engage with its Java user community and gather feedback on its Java offerings.

Highlights of JavaOne


JavaOne has many highlights, including keynote speeches by industry leaders, technical sessions by Java experts, and product demos by Oracle and other companies. Some of the most notable highlights of JavaOne include:

Keynote speeches


JavaOne features keynote speeches by industry leaders, such as James Gosling (the creator of Java), Mark Reinhold (Chief Architect of the Java Platform), and Thomas Kurian (President of Product Development at Oracle). These speeches provide insights into the future of Java and the latest trends in software development.

Technical sessions


JavaOne offers a wide range of technical sessions, covering topics such as Java development tools, cloud computing, and mobile development. These sessions are presented by Java experts and provide attendees with in-depth knowledge and practical skills.

Hands-on labs


JavaOne also offers hands-on labs, where attendees can get hands-on experience with the latest Java technologies and tools. These labs are led by Java experts and provide attendees with practical skills that they can use in their own projects.

Product demos


JavaOne features product demos by Oracle and other companies, showcasing the latest Java-related products and services. These demos provide attendees with a first-hand look at the latest innovations in Java technology.

Conclusion


JavaOne is a premier technology conference for Java developers and enthusiasts. The conference provides a forum for learning, networking, and sharing experiences. With its keynote speeches, technical sessions, hands-on labs, and product demos, JavaOne offers a unique opportunity for Java developers to stay up-to-date with the latest Java technologies and best practices.

Friday, April 14, 2023

Bigtable Pagination in Java

Consider a set of rows stored in Bigtable table called “people”:

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

My objective is to be able to paginate a few records at a time, say with each page containing 4 records:

Page 1:

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

Page 2:

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

Page 3:

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

High-Level Approach


A high level approach to doing this is to introduce two parameters:

◉ Offset — the point from which to retrieve the records.
◉ Limit — the number of records to retrieve per page

Limit in all cases is 4 in my example. Offset provides some way to indicate where to retrieve the next set of records from. Bigtable orders the record lexicographically using the key of each row, so one way to indicate offset is by using the key of the last record on a page. Given this, and using a marker offset of empty string for the first page, offset and record for each page looks like this:

Page 1 — offset: “”, limit: 4

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

Page 2 — offset: “person#id-004”, limit: 4

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

Page 3 — offset: “person#id-008”, limit: 4

Oracle Java Prep, Oracle Java Preparation, Java Guides, Java Learning, Java Career, Java Jobs, Java Skills

The challenge now is in figuring out how to retrieve a set of records given a prefix, an offset, and a limit.

Retrieving records given a prefix, offset, limit


Bigtable java client provides a “readRows” api, that takes in a Query and returns a list of rows.

import com.google.cloud.bigtable.data.v2.BigtableDataClient
import com.google.cloud.bigtable.data.v2.models.Query
import com.google.cloud.bigtable.data.v2.models.Row
 
val rows: List<Row> = bigtableDataClient.readRows(query).toList()

Now, Query has a variant that takes in a prefix and returns rows matching the prefix:

import com.google.cloud.bigtable.data.v2.BigtableDataClient
import com.google.cloud.bigtable.data.v2.models.Query
import com.google.cloud.bigtable.data.v2.models.Row
 
val query: Query = Query.create("people").limit(limit).prefix(keyPrefix)
val rows: List<Row> = bigtableDataClient.readRows(query).toList()

This works for the first page, however, for subsequent pages, the offset needs to be accounted for.

A way to get this to work is to use a Query that takes in a range:

import com.google.cloud.bigtable.data.v2.BigtableDataClient
import com.google.cloud.bigtable.data.v2.models.Query
import com.google.cloud.bigtable.data.v2.models.Row
import com.google.cloud.bigtable.data.v2.models.Range
 
val range: Range.ByteStringRange = 
    Range.ByteStringRange
        .unbounded()
        .startOpen(offset)
        .endOpen(end)
 
val query: Query = Query.create("people")
                    .limit(limit)
                    .range(range)

The problem with this is to figure out what the end of the range should be. This is where a neat utility that the Bigtable Java library provides comes in. This utility given a prefix of “abc”, calculates the end of the range to be “abd”

import com.google.cloud.bigtable.data.v2.models.Range
 
val range = Range.ByteStringRange.prefix("abc")

Putting this all together, a query that fetches paginated rows at an offset looks like this:

val query: Query =
    Query.create("people")
        .limit(limit)
        .range(Range.ByteStringRange
            .prefix(keyPrefix)
            .startOpen(offset))
 
val rows: List<Row> = bigtableDataClient.readRows(query).toList()

When returning the result, the final key needs to be returned so that it can be used as the offset for the next page, this can be done in Kotlin by having the following type:

data class Page<T>(val data; List<T>, val nextOffset: String)

Source: javacodegeeks.com

Wednesday, April 12, 2023

Quiz yourself: Using the stream methods dropWhile and takeWhile in Java

Quiz Yourself, Oracle Java Certification, Oracle Java Tutorial and Materials, Java Guides, Java Learning, Java Skills, Java Jobs


Given the following method fragment

Collection<Integer> c = Set.of(1,2,3,4,5,6,7); // line 1
var r = c.stream()
  .dropWhile(e -> e < 5)
  .takeWhile(e -> e < 7)
  .count(); // line 2
System.out.println(r);

Which statement is correct? Choose one.

A. Line 1 will cause an error.

B. Replacing the second statement with the following code would produce the same output:

var r = c.stream()
   .takeWhile(e -> e < 7)
   .dropWhile(e -> e < 5)
   .count(); // line 2

C. Replacing the second statement with the following code would produce the same output:

var r = c.stream()
  .filter(e -> !(e < 5))
  .filter(e -> e < 7)
  .count(); // line 2

D. The code may print 0.

E. The code may print 7.

F.     The code always prints 2.

Answer. This question explores some foundational concepts for collections and streams and, in particular, the Stream API’s dropWhile and takeWhile methods. These methods were added in Java 9 and are legitimate territory for the Java 11 and Java 17 exams.

Consider the behavior of the dropWhile and takeWhile methods. Both take a single argument of type Predicate and both methods are intermediate operations that return a new stream.

The dropWhile method, when applied to an ordered stream, starts at the beginning of the stream and tests, in order, data items of the stream it’s reading from. If elements pass the test, they are dropped. So far, this behavior seems similar to that of the filter method, except the behavior removes items that pass the test rather than keeping items that pass the test. However, as soon as an item fails the test, dropWhile becomes a simple pass-through, and all subsequent items will continue down the stream without being tested. The documentation describes this as “dropping the longest prefix of elements.”

Therefore, if an ordered stream contains the following (with the parade of items progressing towards the right, so that 1 is the head of the stream)

10 9 8 7 6 5 4 3 2 1

and you apply dropWhile(e -> e < 5), the output of the dropWhile will be a stream such as the following

10 9 8 7 6 5

If you apply the same dropWhile to this sequence

10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1

the result will be a stream such as the following

10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5

Notice that the result still contains the second occurrences of 1 through 4 (and if more of these occurred later, they too would be present in the result).

The behavior of the takeWhile operation has parallels to this, except that the resulting stream ends as soon as any item in the stream fails the test specified in the predicate. So, if you start with this stream

10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1

and apply takeWhile(e -> e < 7) to it, the result is a stream like the following

6 5 4 3 2 1

Therefore, if you chain the dropWhile(e -> e < 5) and takeWhile(e -> e < 7) operations, and apply them to an ordered stream that looks like the following

7 6 5 4 3 2 1

the dropWhile(e -> e < 5) would yield

7 6 5

and then the takeWhile(e -> e < 7) would operate on that stream and produce

6 5

If you then count the elements, you’d get the value 2.

This is all very nice, except for two problems. First, the order of items drawn from most Set objects is not guaranteed and typically does not match the order in which the items were added to the set. The second problem is you don’t have an ordered stream and as mentioned, the discussion above applies only to ordered streams.

Consider why these two things are true. A set, from a math perspective, has the task of rejecting duplicate elements, but it does not have the task of maintaining user-defined order. (Note that some of Java’s Set implementations do maintain user-defined order, for example, the TreeSet. But the order in this case still isn’t the order in which items were added; it’s literally an ordering that applies to the elements.) The documentation for the Set interface describes the effect of Set.of and Set.copyOf with the following caveat:

The iteration order of set elements is unspecified and is subject to change.

Shortly, you’ll see why this is critical and contradicts the discussion above.

Some stream objects are considered to be ordered and others are not. If you have a source of data that has a meaningful (user-controllable) order, such as the elements of a List or the computations of Stream.iterate (which calculates the next element of a stream from the predecessor), the stream starts out ordered.

Meanwhile, the documentation for dropWhile states

If this stream is unordered, and some (but not all) elements of this stream match the given predicate, then the behavior of this operation is nondeterministic; it is free to drop any subset of matching elements (which includes the empty set).

The documentation for takeWhile has an equivalent statement.

In this quiz question, you have an unordered stream; therefore, you have no basis for making reliable predictions about what it will do. (If you try this code, it will behave consistently, but “It always works for me!” is not a sound way to create production quality code. You must be able to guarantee that it will still work when implementations change, and for that, you must take seriously the limitations called out in the documentation.)

If you don’t even know that this code will behave consistently, you cannot claim to have other code that will behave the same way, nor can you assert that it will always do anything in particular. For this reason, options B, C, and F are all incorrect.

This leaves you to consider whether line 1 compiles, and if so, what the code might possibly output.

Option A claims the code will not compile. There are three ways that might seem like a tempting option.

◉ Can you put primitives into the argument for Set.of? Yes, you can because they will be autoboxed to Integer type. Further, this is consistent with the variable type (Collection<Integer>) to which the result is assigned.

◉ The assignment of the Set.of<Integer> created the variable of type Collection<Integer>. This succeeds because Set is a subinterface of Collection, and the generic types are both Integer.

◉ There would be an exception at runtime if you call Set.of with duplicate values in the argument list. In this case, the arguments are all distinct, so there’s no problem.

From this you know that there are no errors, and option A is also incorrect.

Option D is correct. The documentation for takeWhile working on an unordered stream states “…it is free to take any subset of matching elements (which includes the empty set).” Therefore, it clearly is permissible for the operation to result in zero surviving elements, which would produce a count of 0.

If you ignore the restrictions of dropWhile and takeWhile and simply consider that you don’t know the iteration order of the elements taken from Set.of, you can still see how unexpected results might be achieved. Imagine that the stream elements from the set arrive in the following order:

6 5 4 3 2 1 7

In this case, the dropWhile(e -> e < 5) part will be false on the first element, and none of the elements will be dropped. After that, the takeWhile(e -> e < 7) part will also be false on the first element (which is still 7). Therefore, zero elements will proceed downstream to the count() operation, and therefore 0 would be printed. Because 7 can’t be printed, you can see that option E is incorrect.

Conclusion. The correct answer is option D.

Source: oracle.com

Friday, April 7, 2023

Quiz yourself: String manipulation and local and instance variables


Imagine that your colleague from the IT department is working on a new chatbot application implementing—among some others—the Command design pattern. The bot’s command objects are created once upon startup and reused through the life of the application. Below is the partial code of the application, in particular, the AboutCommand interface, which must return the text Copyright (c) 2023, http://mycompany.com.

Quiz Yourself, String Manipulation, Local Instance Variables, Oracle Java Tutorial and Materials, Java Career, Java Skills, Java Jobs, Java Certification, Java String, Oracle Java Tutorial and Materials

public interface Command {
  String execute(Context c);
}
public class AboutCommand implements Command {
  private String url;
  public AboutCommand(String s) {
    url = s;
  }
  public String execute(Context c) {
    url = url.substring(0, url.lastIndexOf("/")); // line 2
    url = url.substring(0, url.lastIndexOf("/")); // line 3
    return "Copyright (c) 2023, " + url; // line 4
  }
}
public class ChatBot {
  public static final String HOME_PAGE = "http://mycompany.com/botapp/index.html";
  private final Command about;
  ChatBot() {
    about = new AboutCommand(HOME_PAGE);
    … // create some more commands
  }
}

Which statement is correct? Choose one.

A. The code is compilable and works as expected.

B. The code is not compilable. To fix the code, the final modifier on the variable HOME_PAGE must be removed.

C. The code is incorrect. To fix it, modify lines 2 and 3 like this:
url.substring(0, url.lastIndexOf("/")); // line 2
url.substring(0, url.lastIndexOf("/")); // line 3

D. The code is incorrect. To fix it, modify lines 2 and 3 like this:
var url = this.url.substring(0, this.url.lastIndexOf("/")); // line 2
url = this.url.substring(0, this.url.lastIndexOf("/")); // line 3

E. The code is incorrect. To fix it, modify lines 2 and 3 like this:
var url = this.url.substring(0, this.url.lastIndexOf("/")); // line 2
url = url.substring(0, url.lastIndexOf("/")); // line 3

Answer. This question investigates aspects of string manipulation, local and instance variables, and logical thinking.

To begin with, option A is incorrect. While the first call of the execute() method will return the required text, the method has side effects. Specifically, it modifies the instance variable. Therefore, a second call will return only Copyright (c) 2023, http:. A third call will throw java.lang.StringIndexOutOfBoundsException.

To correct this, you could either recalculate the message each time without changing the value of the field named url, or you could calculate the message once and store the result for future use. The latter would be more efficient. Let’s investigate the other options to see if any achieve this result.

Option B is incorrect and entirely irrelevant to the problem you have. At no point does any of the code shown in the question attempt to reassign the HOME_PAGE constant, so the final modifier cannot be the cause of any problems.

Option C is also incorrect. It avoids reassigning the url field, which seems like a step forward. However, strings are immutable, so line 4 will return the initial unchanged value. Line 2 will create a new string containing the text http://mycompany.com/botapp, but that string is immediately abandoned because the reference to it (which is carried as the return value of the lastIndexOf method) is not stored anywhere. Line 3 simply repeats the process, and the new string is abandoned again. Therefore, the result will be Copyright (c) 2023, http://mycompany.com/botapp/index.html.

Quiz Yourself, String Manipulation, Local Instance Variables, Oracle Java Tutorial and Materials, Java Career, Java Skills, Java Jobs, Java Certification, Java String, Oracle Java Tutorial and Materials
In addition, option D is incorrect. It introduces a new local variable, String url. Line 2 creates a new string (as in option C) by removing the /index.html part from the original text. This new string is stored in the local variable called url. However, line 3 simply repeats the behavior of line 2, because it reads its starting value from the field called url, not the value created by line 2. Consequently, this version produces the message Copyright (c) 2023, http://mycompany.com/botapp.

Option E is correct. Lines 2 and 3 both store the result of their operations, and line 3 uses the result of line 2 as its starting point. As a result, the code cuts off two elements from the url: /index.html first and then /botapp. Critically, this code does not modify the instance variable, so each subsequent run of the execute() method will return the desired text: Copyright (c) 2023, http://mycompany.com.

To be clear, this is not an efficient approach to coding this problem. It would be better to calculate the result once and store it for reuse. The following code, used as a constructor for the AboutCommand class, would achieve that:

public AboutCommand(String s) {
  url = s.substring(0, s.lastIndexOf("/"));
  url = url.substring(0, url.lastIndexOf("/"));
  url = "Copyright (c) 2023, " + url;
}

Given this initialization, the execute method would simply look like the following:

public String execute(Context c) {
  return url;
}

There’s a final note to mention here. Were you wondering about that constructor argument Context c? In quiz questions, you should assume that there’s enough code surrounding what’s shown, or enough context, to allow the code to work if it can work. If there’s a problem you are expected to recognize, it’ll be in the code you are shown, not in something that’s not shown.

The Java 17 exam objectives include some notes about the assumptions of this kind that you are expected to make. The notes are present for the Java 8 exam too, but for some reason, possibly simple oversight, they weren’t included for the Java 11 version.

Conclusion. The correct answer is option E.

Source: oracle.com

Wednesday, April 5, 2023

What is Oracle Java SE? Understanding the Most Popular Programming Language

Oracle Java SE, Oracle Java Tutorial and Materials, Java Certification, Java Career, Java Skills, Java Jobs

Java is one of the most popular programming languages in the world, and for good reason. It is a versatile and efficient language that can be used to create applications, games, and even entire operating systems. In this article, we will explore Oracle Java SE, the latest version of Java, and what it can do.

What is Java?


Java is an object-oriented programming language that was developed in the mid-1990s by Sun Microsystems, which was later acquired by Oracle Corporation. Java is widely used to create web applications, mobile applications, and enterprise software. It is also used in the creation of games and other interactive content. Java is platform-independent, which means that it can run on any operating system that has a Java Virtual Machine (JVM) installed.

What is Oracle Java SE?


Oracle Java SE is the latest version of Java that is offered by Oracle Corporation. It is a comprehensive development kit that includes everything needed to develop and run Java applications. Oracle Java SE includes a compiler, a runtime environment, and a set of libraries that are used to create Java applications. The current version of Oracle Java SE is version 17.

What are the features of Oracle Java SE?


Oracle Java SE includes a wide range of features that make it a powerful and versatile programming language. Some of the key features of Oracle Java SE include:

Object-oriented programming

Java is an object-oriented programming language, which means that it is designed around objects and their interactions. This makes it easy to create complex applications and systems that are modular and easy to maintain.

Garbage collection

Java includes a garbage collector that automatically frees up memory that is no longer being used. This makes it easier to write applications that are memory-efficient and reduces the risk of memory leaks.

Exception handling

Java includes a robust exception handling mechanism that makes it easy to handle errors and unexpected events. This helps to make Java applications more robust and reliable.

Multithreading

Java supports multithreading, which means that it is possible to create applications that can perform multiple tasks simultaneously. This makes it possible to create applications that are more responsive and efficient.

Security

Java includes a number of security features that help to make it a safe and secure programming language. For example, Java applets run in a sandboxed environment that prevents them from accessing resources on the user's computer.

How is Oracle Java SE used?


Oracle Java SE is used in a wide range of applications and systems. It is commonly used to develop enterprise applications, web applications, mobile applications, and games. Java is also used in the development of Android applications, which makes it one of the most widely used programming languages in the world.

How do I get started with Oracle Java SE?


To get started with Oracle Java SE, you will need to download and install the Java Development Kit (JDK). The JDK includes everything you need to start developing Java applications, including the Java compiler and runtime environment. Once you have installed the JDK, you can use an integrated development environment (IDE) such as Eclipse or NetBeans to start writing Java code.

What are the advantages of using Oracle Java SE?


There are many advantages to using Oracle Java SE for software development. Some of the key advantages include:

Portability

Java is a platform-independent programming language, which means that Java applications can run on any operating system that has a Java Virtual Machine (JVM) installed. This makes it easy to develop applications that can run on multiple platforms.

Robustness

Java includes a number of features that make it a robust and reliable programming language. For example, the exception handling mechanism makes it easy to handle errors and unexpected events, while the garbage collector helps to prevent memory leaks.

Security

Java includes a number of built-in security features that make it a safe and secure programming language. Java applets, for example, run in a sandboxed environment that prevents them from accessing resources on the user's computer without their explicit permission. This helps to protect users from potential security threats, such as malware and viruses.

Large community and resources

Java has a large and active community of developers and users, which means that there are many resources available for learning and development. There are numerous online communities, forums, and blogs where developers can get help with their code and stay up to date on the latest developments in the Java ecosystem. Additionally, there are many libraries and frameworks available that can help speed up the development process and make it easier to build high-quality applications.

High demand and job opportunities

Java is one of the most widely used programming languages in the world, which means that there is a high demand for developers with Java skills. This translates into a wide range of job opportunities and career paths for developers who are skilled in Java. From web development and enterprise software to mobile app development and gaming, there are many different industries and applications where Java is used.

Conclusion


In conclusion, Oracle Java SE is a powerful and versatile programming language that is widely used in software development. With its object-oriented design, garbage collection, exception handling, multithreading, and security features, Java is a reliable and efficient language that can be used to create a wide range of applications and systems. For developers who are interested in learning Java, there are many resources available, including online communities, forums, and blogs. And with its high demand and job opportunities, Java is a great language for developers who want to build a career in software development.

Monday, April 3, 2023

Java Concurrency: The Lock interface

Java Concurrency, Lock Interface, Oracle Java Certification, Java Guides, Java Prep, Java Preparation, Java Career, Java Skills, Java Jobs, Java Learning, Java Guides

Previously we implemented a thread safe counter using synchronized. We would like to swift from synchronized blocks to something more flexible with more features, this is were locks are of use. On this blog we will focus on Java’s Lock interface.

The Lock interface supports three forms of lock acquisition interruptible, non-interruptible, and timed.

We can acquire locks between threads. A thread will wait until the lock is released from the thread holding the lock:

@Test
    void lock() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            lock.lock();
            log.info("Acquired delayed");
            lock.unlock();
        });
 
        lock.lock();
        withDelayedLock.start();
 
        Thread.sleep(500);
        log.info("Will release");
        lock.unlock();
        withDelayedLock.join();
    }

Since the threads blocks on the lock we might as well interrupt the thread. In this case we can lock lockInterruptibly, and apply any logic in case of an Interrupt:

@Test
    void lockInterruptibly() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                log.error("interrupted while waiting",e);
            }
        });
 
        lock.lock();
        withDelayedLock.start();
        withDelayedLock.interrupt();
        lock.unlock();
        withDelayedLock.join();
    }

A thread can also try to acquire a lock which is already acquired, and exit immediately instead of blocking.

@Test
    void tryLock() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            boolean locked = lock.tryLock();
            assertFalse(locked);
        });
 
        lock.lock();
        withDelayedLock.start();
        Thread.sleep(500);
        lock.unlock();
        withDelayedLock.join();
    }

Also a time period can be specified until the lock is acquired.

@Test
    void tryLockTime() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            try {
                boolean locked = lock.tryLock(100, TimeUnit.MILLISECONDS);
                assertFalse(locked);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
 
        lock.lock();
        withDelayedLock.start();
        Thread.sleep(500);
        lock.unlock();
        withDelayedLock.join();
    }

Another thing of importance with locks is memory.

From the documentation

All {@code Lock} implementations <em>must</em> enforce the same
* memory synchronization semantics as provided by the built-in monitor
* lock, as described in
* Chapter 17 of
* <cite>The Java Language Specification</cite>:
*
<ul>*
    <li>A successful {@code lock} operation has the same memory
* synchronization effects as a successful <em>Lock</em> action.
*</li>
    <li>A successful {@code unlock} operation has the same
* memory synchronization effects as a successful <em>Unlock</em> action.
*</li>
</ul>

In order for the results to be flushed on the main memory we need to use lock and unlock.
Take the following example

private Lock lock = new ReentrantLock();
    private String threadAcquired = "main";
 
    @Test
    void wrongMemoryVisibility() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            lock.lock();
            try {
                threadAcquired = "delayed";
                System.out.println("Acquired on delayed");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
 
        });
 
        lock.lock();
        try {
            withDelayedLock.start();
            Thread.sleep(500);
        } finally {
            lock.unlock();
        }
 
        while (true) {
            System.out.println("Currently acquired " + threadAcquired);
            if (threadAcquired.equals("delayed")) {
                break;
            }
        }
 
        withDelayedLock.join();
        threadAcquired = "main";
    }

We print the variable threadAcquired by the main thread while it is changed by the thread with a Delayed lock.

After a few runs a situation where the threadAcquired variable has a stale value on the main thread will appear.

Acquired on delayed
Currently acquired main

We did this on purpose, on a real world problem we should not access variables like threadAcquired without proper synchronisation, for example we should have acquired the lock first, this way the memory would be synchronised and we would have an up to date value.

@Test
    void correctMemoryVisibility() throws InterruptedException {
        Thread withDelayedLock = new Thread(() -> {
            lock.lock();
            try {
                threadAcquired = "delayed";
                System.out.println("Acquired on delayed");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
 
        });
 
        lock.lock();
        try {
            withDelayedLock.start();
            Thread.sleep(500);
        } finally {
            lock.unlock();
        }
 
        while (true) {
            lock.lock();
            try {
                System.out.println("Currently acquired " + threadAcquired);
                if (threadAcquired.equals("delayed")) {
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
 
        withDelayedLock.join();
    }

That’s all for now, we will later proceed on different type of locks and the Condition interface.

Source: javacodegeeks.com