Wednesday, March 30, 2022

Java 15 – Text blocks

Text blocks are all about writing multi-line strings in a clean and readable way. This was added as part of JEP 378 in Java 15. One can read the detailed information of the feature from the JEP details.

In this post, I will show you how to use text blocks and some things to keep in mind while using them.

Firstly, the boundary of a text block is defined by using """ and """. Anything between this is your string literal as shown below

String userDetailSQL = """

    SELECT u.username, u.first_name, u.last_name, r.privilege

    FROM app_user u

    LEFT OUTER JOIN app_user_privilege up ON up.username = r.username

    WHERE u.username = ?

    """;

System.out.print(userDetailSQL);

The above would simply print the query defined in userDetailSQL as shown below

Java 15 – Text blocks, Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Certification, Oracle Java Learning

Some important things to note here

Point 1


A new line is added at the end of the string only if the closing """ is in the new line. So if we had the below:

String userDetailSQL = """
    SELECT u.username, u.first_name, u.last_name, r.privilege
    FROM app_user u
    LEFT OUTER JOIN app_user_privilege up ON up.username = r.username
    WHERE u.username = ?""";
System.out.print(userDetailSQL);

The output would have been (see the absence of an extra line above “Process finished…”

Java 15 – Text blocks, Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Certification, Oracle Java Learning
String printed without the new line appended to the string

Point 2


Suppose we were to write the query with indentation as shown below:

String userDetailSQLWithWhitespace = """
    SELECT
        u.username,
        u.first_name,
        u.last_name,
        r.privilege
    FROM app_user u
    LEFT OUTER JOIN app_user_privilege up 
        ON up.username = r.username
    WHERE u.username = ? 
    """;
System.out.println(userDetailSQLWithWhitespace);

The compiler will preserve any indentation done away from the line drawn upwards vertically starting from the closing """. So the output in the above case can be:

Java 15 – Text blocks, Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Certification, Oracle Java Learning
The string printed by preserving the indents

Point 3


We can force a string to be indented by using the indent method as shown below:

String userDetailSQL = """
        SELECT u.username, u.first_name, u.last_name, r.privilege
        FROM app_user u
        LEFT OUTER JOIN app_user_privilege up ON up.username = r.username
        WHERE u.username = ?
        """;
System.out.println(userDetailSQL.indent(3));

And the output of this would be:

Java 15 – Text blocks, Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Certification, Oracle Java Learning
Spaces have been added to adjust the text based on the indent

Point 4


You can create an empty text block, though its not going to be useful. You would do it as:

//Correct empty text block
String emptyTextBlock = """
    """;

And not as

//Wrong empty text block
String emptyTextBlockWrong = """""";

Point 5


You can concatenate text blocks with normal strings and also create formatted text blocks as shown below:

IntStream.range(1, 100).forEach(
    i -> {
        String message = """
                INSERT INTO app_user(username, first_name, last_name, password)\s
                VALUES('user%d', 'First %d', 'Last %d', '12345-%d');
                """;
        System.out.println(String.format(message, i, i, i,i));
    }
);

Source: javacodegeeks.com

Friday, March 25, 2022

Synchronization in Java, Part 3: Atomic operations and deadlocks

This third article in a series on thread synchronization describes volatile fields, final variables, atomic operations, deadlocks, the deprecated stop and suspend methods, and on-demand initializations.

The first article in this series on thread synchronization covered the fundamentals of race conditions, lock objects, condition objects, and the await, signal, and signalAll methods. The second article addressed intrinsic locks, the synchronized keyword, synchronized blocks, ad hoc locks, and the concept of monitors.

This series conclusion describes volatile fields, final variables, atomic operations, deadlocks, the deprecated stop and suspend methods, and on-demand initializations.

Volatile fields

Sometimes, it seems excessive to pay the cost of synchronization just to read or write an instance field or two. After all, what can go wrong? Unfortunately, with modern processors and compilers, there is plenty of room for error.

◉ Computers with multiple processors can temporarily hold memory values in registers or local memory caches. As a consequence, threads running in different processors may see different values for the same memory location!

◉ Compilers can reorder instructions for maximum throughput. Compilers won’t choose an ordering that changes the meaning of the code, but they assume that memory values are changed only when there are explicit instructions in the code. However, a memory value can be changed by another thread!

If you use locks to protect code that can be accessed by multiple threads, you won’t have these problems. Compilers are required to respect locks by flushing local caches as necessary and not inappropriately reordering instructions. The details are explained in the Java Memory Model and Thread Specification developed by JSR 133. Much of the specification is highly complex and technical, but the document also contains a number of clearly explained examples. (“JSR 133 (Java Memory Model) FAQ” is a more accessible overview article by Jeremy Manson and Brian Goetz.)

Oracle Java Language Architect Brian Goetz coined the following synchronization motto: “If you write a variable which may next be read by another thread, or you read a variable which may have last been written by another thread, you must use synchronization.”

The volatile keyword offers a lock-free mechanism for synchronizing access to an instance field. If you declare a field as volatile, the compiler and the virtual machine take into account that the field may be concurrently updated by another thread.

For example, suppose an object has a Boolean flag done that is set by one thread and queried by another thread. As already discussed, you can use a lock, as follows:

private boolean done;

public synchronized boolean isDone() { return done; }

public synchronized void setDone() { done = true; }

Perhaps it is not a good idea to use the intrinsic object lock. The isDone and setDone methods can block if another thread has locked the object. If that is a concern, you can use a separate lock just for this variable. But this is getting to be a lot of trouble.

In this case, it is reasonable to declare the field as volatile.

private volatile boolean done;

public boolean isDone() { return done; }

public void setDone() { done = true; }

The compiler will insert the appropriate code to ensure that a change to the done variable in one thread is visible from any other thread that reads the variable.

Warning: Variables that are volatile do not provide any atomicity. For example, the method

public void flipDone() { done = !done; } // not atomic

is not guaranteed to flip the value of the field. There is no guarantee that the reading, flipping, and writing will be uninterrupted.

Final variables

As you saw in the preceding section, you cannot safely read a field from multiple threads unless you use locks or the volatile modifier.

There is one other situation in which it is safe to access a shared field: when it is declared final. Consider

final var accounts = new HashMap<String, Double>();

Other threads get to see the accounts variable after the constructor has finished.

Without using final, you would have no guarantee that other threads would see the updated value of accounts—they might all see null, not the constructed HashMap.

Of course, the operations on the map are not thread-safe. If multiple threads mutate and read the map, you still need synchronization.

Atomics

You can declare shared variables as volatile provided you perform no operations other than assignment.

There are several classes in the java.util.concurrent.atomic package that use efficient machine-level instructions to guarantee the atomicity of other operations without using locks. For example, the AtomicInteger class has methods incrementAndGet and decrementAndGet that atomically increment or decrement an integer. For example, you can safely generate a sequence of numbers like this.

public static AtomicLong nextNumber = new AtomicLong(); // in some thread. . .

long id = nextNumber.incrementAndGet();

The incrementAndGet method atomically increments the AtomicLong and returns the postincrement value. That is, the operations of getting the value, adding 1, setting it, and producing the new value cannot be interrupted. It is guaranteed that the correct value is computed and returned, even if multiple threads access the same instance concurrently.

There are methods for atomically setting, adding, and subtracting values, but if you want to make a more complex update, you have to use the compareAndSet method. For example, suppose you want to keep track of the largest value that is observed by different threads. The following won’t work:

public static AtomicLong largest = new AtomicLong();

// in some thread. . .

largest.set(Math.max(largest.get(), observed)); // ERROR--race condition!

This update is not atomic. Instead, provide a lambda expression for updating the variable, and the update is done for you. In the example, you can call

largest.updateAndGet(x -> Math.max(x, observed));

or

largest.accumulateAndGet(observed, Math::max);

The accumulateAndGet method takes a binary operator that is used to combine the atomic value and the supplied argument. There are also methods getAndUpdate and getAndAccumulate that return the old value.

These methods are also provided for the classes AtomicInteger, AtomicIntegerArray, AtomicIntegerFieldUpdater, AtomicLongArray, AtomicLongFieldUpdater, AtomicReference, AtomicReferenceArray, and AtomicReferenceFieldUpdater.

When you have a very large number of threads accessing the same atomic values, performance suffers because the optimistic updates require too many retries. The LongAdder and LongAccumulator classes solve this problem. A LongAdder is composed of multiple variables whose collective sum is the current value. Multiple threads can update different summands, and new summands are automatically provided when the number of threads increases. This is efficient in the common situation where the value of the sum is not needed until after all work has been done. The performance improvement can be substantial.

If you anticipate high contention, you should simply use a LongAdder instead of an AtomicLong. The method names are slightly different. Call increment to increment a counter or add to add a quantity, and call sum to retrieve the total.

var adder = new LongAdder(); 

or (. . .)

   pool.submit(() ->

   {

      while (. . .)

      {

           . . .

            if (. . .) adder.increment(); 

         }

}); 

...

long total = adder.sum();long total = adder.sum();

Of course, the increment method does not return the old value. Doing that would undo the efficiency gain of splitting the sum into multiple summands.

The LongAccumulator generalizes this idea to an arbitrary accumulation operation. In the constructor, you provide the operation as well as its neutral element. To incorporate new values, call accumulate. Call get to obtain the current value. The following has the same effect as a LongAdder:

var adder = new LongAccumulator(Long::sum, 0); 

// in some thread. . . 

adder.accumulate(value);

Internally, the accumulator has variables a1, a2. . .an. Each variable is initialized with the neutral element (0 in this example).

When accumulate is called with value v, one of them is atomically updated as ai = ai op v, where op is the accumulation operation written in infix form. In this example, a call to accumulate computes ai = ai + v for some i.

The result of get is a1 op a2 op...op an. In this example, that is the sum of the accumulators a1 + a2 +...+ an.

If you choose a different operation, you can compute maximum or minimum. In general, the operation must be associative and commutative. That means that the final result must be independent of the order in which the intermediate values were combined.

There are also DoubleAdder and DoubleAccumulator that work in the same way, except with double values.

Deadlocks

Locks and conditions cannot solve all problems that might arise in multithreading. Consider the following situation in a banking application:

◉ Account 1: $200.

◉ Account 2: $300.

◉ Thread 1: Transfer $300 from Account 1 to Account 2.

◉ Thread 2: Transfer $400 from Account 2 to Account 1.

As Figure 1 indicates, Thread 1 and Thread 2 are clearly blocked. Neither can proceed because the balances in Account 1 and Account 2 are insufficient.

Synchronization in Java, Core Java, Oracle Java Certification, Java Preparation, Java Learning, Oracle Java Career, Java Jobs, Java Skills
Figure 1. A deadlock situation

It is possible that all threads get blocked because each is waiting for more money. Such a situation is called a deadlock, and unless there are handlers to detect the situation, the program will hang.

If the program hangs, press Ctrl+\. You will get a thread dump that lists all threads. Each thread has a stack trace, telling you where it is currently blocked. Alternatively, run jconsole and consult the Threads panel (see Figure 2).

Synchronization in Java, Core Java, Oracle Java Certification, Java Preparation, Java Learning, Oracle Java Career, Java Jobs, Java Skills
Figure 2. The Threads panel in jconsole

Consider the following sample scenario of a developing deadlock:

◉ Account 1: $1,990.
◉ All other accounts: $990 each.
◉ Thread 1: Transfer $995 from Account 1 to Account 2.
◉ All other threads: Transfer $995 from their account to another account.

Clearly, all threads but Thread 1 are blocked, because there isn’t enough money in their accounts. Thread 1 proceeds, and afterward, you have the following situation:

◉ Account 1: $995
◉ Account 2: $1,985
◉ All other accounts: $990 each

Then, Thread 1 calls signal. The signal method picks a thread at random to unblock. Suppose it picks Thread 3. That thread is awakened, finds that there isn’t enough money in its account, and calls await again. But Thread 1 is still running. A new random transaction is generated, say,

◉ Thread 1: Transfer $997 from Account 1 to Account 2.

Now, Thread 1 also calls await, and all threads are blocked. The system has deadlocked.

The culprit here is the call to signal, which unblocks only one thread, and it may not pick the thread that is essential to make progress. (In this scenario, Thread 2 must proceed to take money out of Account 2.)

Unfortunately, there is nothing in the Java programming language to avoid or break these deadlocks. You must design your program’s logic to ensure that a deadlock situation cannot occur.

Why the stop and suspend methods are deprecated


The initial release of Java defined a stop method that simply terminates a thread and a suspend method that blocks a thread until another thread calls resume. The stop and suspend methods have something in common: Both methods attempt to control the behavior of a given thread without the thread’s cooperation.

The stop, suspend, and resume methods have been deprecated. The stop method is inherently unsafe, and experience has shown that the suspend method frequently leads to deadlocks. Why are these methods problematic? What can you do to avoid problems?

The stop method. This method terminates all pending methods, including the run method. When a thread is stopped, it immediately gives up the locks on all objects that it has locked. This can leave objects in an inconsistent state.

For example, suppose a TransferRunnable is stopped in the middle of moving money from one bank account to another, after the withdrawal and before the deposit. Now the bank object is damaged. Since the lock has been relinquished, the damage is observable from the other threads that have not been stopped.

When one thread wants to stop another thread, it has no way of knowing when the stop method is safe and when it leads to damaged objects. Therefore, the stop method has been deprecated. You should interrupt a thread when you want it to stop. The interrupted thread can then stop when it knows it is safe to do so.

By the way, some authors claim that the stop method has been deprecated because it can cause objects to be permanently locked by a stopped thread. However, that claim is not valid. A stopped thread exits all synchronized methods it has called—technically, by throwing a ThreadDeath exception. As a consequence, the thread relinquishes the intrinsic object locks that it holds.

The suspend method. Unlike stop, suspend won’t damage objects. However, if you suspend a thread that owns a lock, the lock is unavailable until the thread is resumed. If the thread that calls the suspend method tries to acquire the same lock, the program deadlocks: The suspended thread waits to be resumed, and the suspending thread waits for the lock.

This situation occurs frequently in graphical user interfaces. Suppose you have a graphical simulation of a bank. A button labeled Pause suspends the transfer threads, and a button labeled Resume resumes them.

pauseButton.addActionListener(event ->
   {
      for (int i = 0; i < threads.length; i++)
         threads[i].suspend(); // don't do this
   });

resumeButton.addActionListener(event ->
   {
      for (int i = 0; i < threads.length; i++)
         threads[i].resume(); 
   });

Suppose a paintComponent method paints a chart of each account, calling a getBalances method to get an array of balances. Both the button actions and the repainting occur in the same thread, the event dispatch thread. Consider the following scenario:

◉ One of the transfer threads acquires the lock of the bank object.
◉ The user clicks the Pause button.
◉ All transfer threads are suspended, but one of them still holds the lock on the bank object.
◉ For some reason, the account chart needs to be repainted.
◉ The paintComponent method calls the getBalances method.
◉ That method tries to acquire the lock of the bank object.

Now the program is frozen. The event dispatch thread can’t proceed because the lock is owned by one of the suspended threads. Thus, the user can’t click the Resume button, and the threads won’t ever resume.

If you want to safely suspend a thread, introduce a suspendRequested variable and test it in a safe place of your run method—in a place where your thread doesn’t lock objects that other threads need. When your thread finds that the suspendRequested variable has been set, it should keep waiting until it becomes available again.

On-demand initialization


Sometimes you have a data structure that you want to initialize only when it is first needed, and you want to ensure that initialization happens exactly once. Instead of designing your own mechanism, make use of the fact that the JVM executes a static initializer exactly once when the class is first used. The JVM ensures this with a lock, so you don’t have to program your own.

public class OnDemandData
{
   // private constructor to ensure only one object is constructed
   private OnDemandData()
   {
      ... 
   }

   public static OnDemandData getInstance()
   {
      return Holder.INSTANCE;  
   }

   // only initialized on first use, i.e. in the first call to getInstance 
   private static Holder
   {
      // VM guarantees that this happens at most once
      static final OnDemandData INSTANCE = new OnDemandData(); 
   }
}

By the way, to use this idiom, you must ensure that the constructor doesn’t throw any exceptions. The JVM will not make a second attempt to initialize the Holder class.

Source: oracle.com

Wednesday, March 23, 2022

Synchronization in Java, Part 2: The synchronized keyword

Java Synchronization, Synchronized Keyword, Core Java, Oracle Java Tutorial and Materials, Oracle Java Career, Java Skills, Java Job

This second article in a series on thread synchronization addresses intrinsic locks, the synchronized keyword, synchronized blocks, and ad hoc locks.

The first article in this series about thread synchronization covered the fundamentals of race conditions, lock objects, condition objects, and the await, signal, and signalAll methods.

This second article addresses intrinsic locks, the synchronized keyword, synchronized blocks, ad hoc locks, and the concept of monitors.

The synchronized keyword

In the preceding article, you saw how to use lock and condition objects. To summarize, the following are the key points about locks and conditions:

◉ A lock protects sections of code, allowing only one thread to execute the code at a time.

◉ A lock manages threads that are trying to enter a protected code segment.

◉ A lock can have one or more associated condition objects.

◉ Each condition object manages threads that have entered a protected code section but that cannot proceed.

The Lock and Condition interfaces give you a high degree of control over locking. However, in most situations, you don’t need that control, because you can use a mechanism that is built into the Java language. Ever since version 1.0, every object in Java has an intrinsic lock. If a method is declared with the synchronized keyword, the object’s lock protects the entire method. That is, to call the method, a thread must acquire the intrinsic object lock. In other words,

public synchronized void method()

{

   method body

}

is the equivalent of

public void method()

{

   this.intrinsicLock.lock();

   try

   {

      method body

   }

   finally

   {

      this.intrinsicLock.unlock();

   }

}

For example, instead of using an explicit lock in the banking application introduced in the first article in this series, you could simply declare the transfer method of the Bank class as synchronized.

The intrinsic object lock has a single associated condition. The wait method adds a thread to the wait set, and the notifyAll and notify methods unblock waiting threads. In other words, calling wait or notifyAll is the equivalent of

intrinsicCondition.await();

intrinsicCondition.signalAll();

Note that the wait, notifyAll, and notify methods are final methods of the Object class. The Condition methods had to be named await, signalAll, and signal so that they didn’t conflict with those methods.

For example, you can implement the Bank class in Java like this.

class Bank

{

   private double[] accounts;

   public synchronized void transfer(int from, int to, int amount)

      throws InterruptedException

   {

      while (accounts[from] < amount)

         wait(); // wait on intrinsic object lock's single condition

      accounts[from] -= amount;

      accounts[to] += amount;

      notifyAll(); // notify all threads waiting on the condition

   }

   public synchronized double getTotalBalance()

   {

      ...

   }

}

As you can see, using the synchronized keyword yields code that is much more concise. Of course, to understand this code, you must know that each object has an intrinsic lock and that the lock has an intrinsic condition. The lock manages the threads that try to enter a synchronized method. The condition manages the threads that have called wait.

It is also legal to declare static methods as synchronized. If such a method is called, it acquires the intrinsic lock of the associated class object. For example, if the Bank class has a static synchronized method, the lock of the Bank.class object is locked when it is called. As a result, no other thread can call this or any other synchronized static method of the same class.

The intrinsic locks and conditions have some limitations. The following are among them:

◉ You cannot interrupt a thread that is trying to acquire a lock.

◉ You cannot specify a timeout when trying to acquire a lock.

◉ Having a single condition per lock can be inefficient.

What should you use in your code: lock and condition objects or synchronized methods? Here are my recommendations.

◉ It is best to not use Lock, Condition, or the synchronized keyword. In many situations, you can use one of the mechanisms of the java.util.concurrent package that do all the locking for you.

◉ If the synchronized keyword works for your situation, by all means, use it. You’ll write less code and have less room for error. Listing 1 shows the bank example from the first article implemented with synchronized methods.

◉ Use Lock and Condition if you really need the additional power that these constructs give you.

Listing 1. synch2/Bank.java

package synch2;

import java.util.*;

/**

 * A bank with a number of bank accounts that uses synchronization primitives.

 */

public class Bank

{

   private final double[] accounts;

   /**

    * Constructs the bank.

    * @param n the number of accounts

    * @param initialBalance the initial balance for each account

    */

   public Bank(int n, double initialBalance)

   {

      accounts = new double[n];

      Arrays.fill(accounts, initialBalance);

   }

   /**

    * Transfers money from one account to another.

    * @param from the account to transfer from

    * @param to the account to transfer to

    * @param amount the amount to transfer

    */

   public synchronized void transfer(int from, int to, double amount)

      throws InterruptedException

   {

      while (accounts[from] < amount)

         wait();

      System.out.print(Thread.currentThread());

      accounts[from] -= amount;

      System.out.printf(" %10.2f from %d to %d", amount, from, to);

      accounts[to] += amount;

      System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());

      notifyAll();

   }

   /**

     * Gets the sum of all account balances.

     * @return the total balance

     */

   public synchronized double getTotalBalance()

   {

      double sum = 0;

      for (double a : accounts) sum += a;

      return sum;

   }

   /**

   * Gets the number of accounts in the bank.

   * @return the number of accounts

   */

   public int size()

   {

      return accounts.length;

   }

}

Synchronized blocks

As you just saw, every Java object has a lock. A thread can acquire the lock by calling a synchronized method. There is a second mechanism for acquiring the lock, which is by entering a synchronized block. When a thread enters a block of the form

synchronized (obj) // this is the syntax for a synchronized block

{

   Critical section

}

then it acquires the lock for obj.

You will sometimes find ad hoc locks, such as

public class Bank

{

   private double[] accounts;

   private Lock lock = new Object();

   ...

   public void transfer(int from, int to, int amount)

   {

      synchronized (lock) // an ad-hoc lock

      {

         accounts[from] -= amount;

         accounts[to] += amount;

      }

      System.out.println(. . .);

   }

}

Here, the lock object is created only to use the lock that every Java object possesses.

With synchronized blocks, be careful about the lock object. For example, the following will not work:

private final String lock = "LOCK";

...

synchronized (lock) { . . . } // Don't lock on string literal!

If this occurs twice in the same program, the locks are the same object since string literals are shared. This can lead to a deadlock. Also, stay away from using primitive type wrappers as locks.

private final Integer lock = new Integer(42); // Don't lock on wrappers

The constructor call new Integer(0) is deprecated, and you don’t want a maintenance programmer to change the call to Integer.valueOf(42). If done twice with the same magic number, the lock will be accidentally shared.

If you need to modify a static field, lock on the specific class, not on the value returned by getClass().

synchronized (MyClass.class) { staticCounter++; } // OK

synchronized (getClass()) { staticCounter++; } // Don't

If the method containing this code is called from a subclass, getClass() returns a different Class object! You are no longer guaranteed mutual exclusion! In general, if you must use synchronized blocks, know your lock object! You must use the same lock for all protected access paths—and nobody else must use your lock.

Sometimes, programmers use the lock of an object to implement additional atomic operations. This practice is known as client-side locking. Consider, for example, the Vector class, which is a list whose methods are synchronized. Now suppose you stored the bank balances in a Vector<Double>. Here is a naïve implementation of a transfer method.

public void transfer(Vector<Double> accounts, int from, int to, int amount) // ERROR

{

   accounts.set(from, accounts.get(from) - amount);

   accounts.set(to, accounts.get(to) + amount);

   System.out.println(. . .);

}

The get and set methods of the Vector class are synchronized, but that doesn’t help. It is entirely possible for a thread to be pre-empted in the transfer method after the first call to get has been completed. Another thread may then store a different value in the same position. However, you can hijack the lock, as follows:

public void transfer(Vector<Double> accounts, int from, int to, int amount)

{

   synchronized (accounts)

   {

      accounts.set(from, accounts.get(from) - amount);

      accounts.set(to, accounts.get(to) + amount);

   }

System.out.println(. . .);

}

This approach works, but it is entirely dependent on the fact that the Vector class uses the intrinsic lock for all of its mutator methods. However, is this really a fact? The documentation of the Vector class makes no such promise. You have to carefully study the source code and hope that future versions do not introduce unsynchronized mutators. As you can see, client-side locking is very fragile and not generally recommended.

Note that the JVM has built-in support for synchronized methods. However, synchronized blocks are compiled into a lengthy sequence of bytecodes to manage the intrinsic lock.

The monitor concept

Locks and conditions are powerful tools for thread synchronization, but they are not very object-oriented. For many years, researchers have looked for ways to make multithreading safe without forcing programmers to think about explicit locks. One of the most successful solutions is the monitor concept that was pioneered by computer scientists Per Brinch Hansen and Tony Hoare in the 1970s. In the terminology of Java, a monitor has the following properties:

◉ A monitor is a class with only private fields.

◉ Each object of that class has an associated lock.

◉ All methods are locked by that lock. In other words, if a client calls obj.method(), the lock for obj is automatically acquired at the beginning of the method call and relinquished when the method returns. Since all fields are private, this arrangement ensures that no thread can access the fields while another thread manipulates them.

◉ The lock can have any number of associated conditions.

◉ Earlier versions of monitors had a single condition, with a rather elegant syntax. You can simply call await accounts[from] >= amount without using an explicit condition variable. However, research showed that indiscriminate retesting of conditions can be inefficient. This problem is solved with explicit condition variables, each managing a separate set of threads.

◉ The Java designers loosely adapted the monitor concept. Every object in Java has an intrinsic lock and an intrinsic condition. If a method is declared with the synchronized keyword, it acts like a monitor method. The condition variable is accessed by calling wait, notifyAll, or notify.

However, a Java object differs from a monitor in three important ways that can compromise thread safety.

◉ Fields are not required to be private.

◉ Methods are not required to be synchronized.

◉ The intrinsic lock is available to clients.

This disrespect for security enraged Dr. Hansen. In a scathing 1999 review of the multithreading primitives in Java, he wrote: “It is astounding to me that Java’s insecure parallelism is taken seriously by the programming community, a quarter of a century after the invention of monitors and Concurrent Pascal. It has no merit.” (“Java’s Insecure Parallelism,” ACM SIGPLAN Notices 34:38–45, April 1999.)

Source: oracle.com

Monday, March 21, 2022

Synchronization in Java, Part 1: Race conditions, locks, and conditions

The first article in this series on thread synchronization covers the fundamentals of race conditions, lock objects, condition objects, and the await, signal, and signalAll methods.

In most practical multithreaded applications, two or more threads need to share access to the same data. What happens if two threads have access to the same object and each calls a method that modifies the state of the object? As you might imagine, the threads can step on each other’s toes. Depending on the order in which the data were accessed, corrupted objects can result. Such a situation is often called a race condition.

An example of a race condition

To avoid corruption of shared data by multiple threads, you must learn how to synchronize the access. In this section, you’ll see what happens if you do not use synchronization. In the following section, you’ll see how to synchronize data access.

The test program is of a simulated bank, where money is transferred between accounts. The bank starts with 100 accounts, each with a $1,000 balance. This example randomly selects the source and destination of the transfer. This could cause problems in a multithreaded system, as you can see in the code for the transfer method of the Bank class.

public void transfer(int from, int to, double amount)

   // CAUTION: unsafe when called from multiple threads

{

   System.out.print(Thread.currentThread());

   accounts[from] -= amount;

   System.out.printf(" %10.2f from %d to %d", amount, from, to);

   accounts[to] += amount;

   System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());

}

Here is the code for the Runnable instances. The run method keeps moving money out of a given bank account. In each iteration, the run method picks a random target account and a random amount, calls transfer on the Bank object, and then sleeps.

Runnable r = () ->

   {

      try

      {

         while (true)

         {

            int toAccount = (int) (bank.size() * Math.random());

            double amount = MAX_AMOUNT * Math.random();

            bank.transfer(fromAccount, toAccount, amount);

            Thread.sleep((int) (DELAY * Math.random()));

         }

      }

      catch (InterruptedException e) {}

   };

When this simulation runs, you do not know how much money is in any specific bank account at any time. But you do know that the total amount of money in all the accounts should remain unchanged because all the program does is move money from one account to another.

At the end of each transaction, the transfer method recomputes the total and prints it. Here is a typical printout.

...

Thread[Thread-11,5,main]

Thread[Thread-12,5,main]

Thread[Thread-14,5,main]

Thread[Thread-13,5,main]

...

Thread[Thread-36,5,main]

Thread[Thread-35,5,main]

Thread[Thread-37,5,main]

Thread[Thread-34,5,main]

Thread[Thread-36,5,main]

...

Thread[Thread-4,5,main]Thread[Thread-33,5,main] 7.31 from 31 to 32 Total Balance: 99979.24

627.50 from 4 to 5 Total Balance: 99979.24

...

As you can see, something is very wrong. For a few transactions, the bank balance remains at $100,000, which is the correct total for 100 accounts of $1,000 each. But after some time, the balance changes slightly. The errors may happen quickly, or it may take a very long time for the balance to become corrupted. This situation does not inspire confidence, and you would probably not want to deposit your hard-earned money in such a bank.

See if you can spot the problems in the code in Listing 1 and the Bank class in Listing 2. The mystery will be unraveled in the next section.

Listing 1. unsynch/UnsynchBankTest.java

package unsynch;

/**

 * This program shows data corruption when multiple threads access a data structure.

 * @version 1.32 2018-04-10

 * @author Cay Horstmann

 */

public class UnsynchBankTest

{

   public static final int NACCOUNTS = 100;

   public static final double INITIAL_BALANCE = 1000;

   public static final double MAX_AMOUNT = 1000;

   public static final int DELAY = 10;

   public static void main(String[] args)

   {

      var bank = new Bank(NACCOUNTS, INITIAL_BALANCE);

      for (int i = 0; i < NACCOUNTS; i++)

      {

         int fromAccount = i;

         Runnable r = () ->

            {

               try

               {

                  while (true)

                  {

                     int toAccount = (int) (bank.size() * Math.random());

                     double amount = MAX_AMOUNT * Math.random();

                     bank.transfer(fromAccount, toAccount, amount);

                     Thread.sleep((int) (DELAY * Math.random()));

                  }

               }

               catch {InterruptedException e)

               {

               }

            };

         var t = new

         t.start();

      }

   }

}

Listing 2. threads/Bank.java

package threads;

import java.util.*;

/**

 * A bank with a number of bank accounts.

 */

public class Bank

{

   private final double[] accounts;

   /**

    * Constructs the bank.

    * @param n the number of accounts

    * @param initialBalance the initial balance for each account

    */

   public Bank(int n, double initialBalance)

   {

      accounts = new double[n];

      Arrays.fill(accounts, initialBalance);

   }

   /**

    * Transfers money from one account to another. 

    * @param from the account to transfer from

    * @param to the account to transfer to

    * @param amount the amount to transfer

    */

   public void transfer(int from, int to, double amount)

   {

      if (accounts[from] < amount) return;

      System.out.print(Thread.currentThread());

      accounts[from] -= amount;

      System.out.printf(" %10.2f from %d to %d", amount, from, to);

      accounts[to] += amount;

      System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());

   }

   /**

    * Gets the sum of all account balances. 

    * @return the total balance

    */

   public double getTotalBalance()

   {

      double sum = 0;

      for (double a : accounts)

         sum += a;

      return sum; }

   /**

    * Gets the number of accounts in the bank.

    * @return the number of accounts

    */

   public int size()

   {

      return accounts.length;

   }

}

The race condition explained

In the previous section, you ran a program in which several threads updated bank account balances. After a while, errors crept into the bank and some amount of money was either lost or spontaneously created. This problem occurred when two threads simultaneously tried to update the same account. Suppose two threads simultaneously carry out the following instruction:

accounts[to] += amount;

The problem is that these are not atomic operations. The instruction might be processed as follows:

◉ Step 1: Load accounts[to] into a register.

◉ Step 2: Add amount.

◉ Step 3: Move the result back to accounts[to].

Suppose the first thread executes Steps 1 and 2, and then it is pre-empted. Suppose the second thread awakens and updates the same entry in the account array. Then, the first thread awakens and completes its Step 3.

That action wipes out the modification of the other thread. As a result, the total is no longer correct (see Figure 1). The test program detects this corruption. (Of course, there is a slight chance of false alarms if the thread is interrupted as it is performing the tests!)

Synchronization in Java, Oracle Java Exam Prep, Core Java, Oracle Java Career, Java Learning, Oracle Java Preparation
Figure 1. Simultaneous access by two threads

By the way, you can peek at the JVM bytecode that executes each statement in the class. Run the command

javap -c -v Bank

to decompile the Bank.class file. For example, the line

accounts[to] += amount;

is translated into the following bytecode:

aload_0
getfield #2; //Field accounts:[D

iload_2
dup2
daload
dload_3
dadd
dastore

What these code snippets mean does not matter. The point is that the increment command is made up of several instructions, and the thread executing them might be interrupted at any instruction.

What is the chance of this corruption occurring? On a modern processor with multiple cores, the risk of corruption is quite high. I boosted the chance of observing the problem on a single-core processor by interleaving the print statements with the statements that update the balance.

If you omit the print statements, the risk of corruption is lower because each thread does so little work before going to sleep again, and it is unlikely that the scheduler will pre-empt it in the middle of the computation. However, the risk of corruption does not go away completely.

If you run many threads on a heavily loaded machine, the program will still fail even after you have eliminated the print statements. The failure may take a few minutes or hours or days to occur. Frankly, there are few things worse in the life of a programmer than an error that manifests itself only irregularly.

The real problem is that the work of the transfer method can be interrupted in the middle. If you could ensure that the method runs to completion before the thread loses control, the state of the Bank object would never be corrupted.

Lock objects


There are two mechanisms for protecting a code block from concurrent access. The Java language provides a synchronized keyword for this purpose, and Java 5 introduced the ReentrantLock class. The synchronized keyword automatically provides a lock as well as an associated condition, which makes it powerful and convenient for most cases that require explicit locking.

However, I believe that it is easier to understand the synchronized keyword after you have seen locks and conditions in isolation. The java.util.concurrent framework provides separate classes for these fundamental mechanisms, which I will explain here. Once you have understood these building blocks, you will learn about the synchronized keyword.

The basic outline for protecting a code block with a ReentrantLock is

myLock.lock(); // a ReentrantLock object
try
{
   <em>critical section</em>
}
finally
{
   myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}

This construct guarantees that only one thread at a time can enter the critical section. As soon as one thread locks the lock object, no other thread can get past the lock statement. When other threads call lock, they are deactivated until the first thread unlocks the lock object.

Caution: It is critically important that the unlock operation is enclosed in a finally clause, because if the code in the critical section throws an exception, the lock must be unlocked. Otherwise, the other threads will be blocked forever.

Note that when you use locks, you cannot use the try-with-resources statement. First off, the unlock method isn’t called close. But even if it were renamed, the try-with-resources statement wouldn’t work because its header expects the declaration of a new variable. However, when you use a lock, you want to keep using the same variable that is shared among threads. Thus, it won’t work.

You can use a lock to protect the transfer method of the Bank class, as in the following:

public class Bank
{
   private Lock bankLock = new ReentrantLock();
   ...
   public void transfer(int from, int to, int amount)
   {
      bankLock.lock();
      try
      {
         System.out.print(Thread.currentThread());
         accounts[from] -= amount;
         System.out.printf(" %10.2f from %d to %d", amount, from, to);
         accounts[to] += amount;
         System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
      }
      finally
      {
         bankLock.unlock();
      }
   }
}

Suppose one thread calls transfer and gets pre-empted before it is done. Suppose a second thread also calls transfer. The second thread cannot acquire the lock and is blocked in the call to the lock method. It is deactivated and must wait for the first thread to finish executing the transfer method. When the first thread unlocks the lock, the second thread can proceed (see Figure 2).

Synchronization in Java, Oracle Java Exam Prep, Core Java, Oracle Java Career, Java Learning, Oracle Java Preparation
Figure 2. Comparison of unsynchronized and synchronized threads

Try it out. Add the locking code to the transfer method and run the program again. You can run it forever, and the bank balance will not become corrupted.

Note that each Bank object has its own ReentrantLock object. If two threads try to access the same Bank object, the lock serves to serialize the access. However, if two threads access different Bank objects, each thread acquires a different lock and neither thread is blocked. This is as it should be, because the threads cannot interfere with one another when they manipulate different Bank instances.

The lock is called reentrant because a thread can repeatedly acquire a lock that it already owns. The lock has a hold count that keeps track of the nested calls to the lock method. The thread must call unlock for every call to lock to relinquish the lock. Because of this feature, code protected by a lock can call another method that uses the same lock.

For example, the transfer method calls the getTotalBalance method, which also locks the bankLock object, which now has a hold count of 2. When the getTotalBalance method exits, the hold count is back to 1. When the transfer method exits, the hold count is 0, and the thread relinquishes the lock.

In general, you will want to protect blocks of code that update or inspect a shared object, so you can be assured that these operations run to completion before another thread can use the same object.

Be careful to ensure that the code in a critical section is not bypassed by throwing an exception. If an exception is thrown before the end of the section, the finally clause will relinquish the lock, but the object may be left in a damaged state.

Condition objects


Sometimes a thread enters a critical section only to discover that it can’t proceed until a condition is fulfilled. Use a condition object to manage threads that have acquired a lock but cannot do useful work. (For historical reasons, condition objects are often called condition variables.)

To refine the simulation of the bank, you do not want to transfer money out of an account that does not have the funds to cover the transfer. However, you cannot use code like

if (bank.getBalance(from) >= amount) bank.transfer(from, to, amount);

It is entirely possible that the current thread will be deactivated between the successful outcome of the test and the call to transfer, as follows:

if (bank.getBalance(from) >= amount)
// thread might be deactivated at this point
bank.transfer(from, to, amount);

By the time the thread is running again, the account balance may have fallen below the withdrawal amount. You must make sure that no other thread can modify the balance between the test and the transfer action. You do so by protecting both the test and the transfer action with a lock, as follows:

public void transfer(int from, int to, int amount)
{
   bankLock.lock();
   try
   {
      while (accounts[from] < amount)
      {
         // wait
         ...
      }
      // transfer funds
      ...
   }
   finally
   {
      bankLock.unlock();
   }
}

What do you do when there is not enough money in the account? You can wait until some other thread has added funds. But this thread has just gained exclusive access to the bankLock, so no other thread has a chance to make a deposit. This is where condition objects come in.

A lock object can have one or more associated condition objects. You obtain a condition object with the newCondition method. It is customary to give each condition object a name that evokes the condition that it represents. For example, the following sets up a condition object to represent the “sufficient funds” condition:

class Bank
{
   private Condition sufficientFunds;
   ...
   public Bank()
   {
      ...
      sufficientFunds = bankLock.newCondition();
   }
}

If the transfer method finds that sufficient funds are not available, it calls

sufficientFunds.await();

The current thread is now deactivated and gives up the lock. This lets in another thread that can, you hope, increase the account balance.

There is an essential difference between a thread that is waiting to acquire a lock and a thread that has called await. Once a thread calls the await method, it enters a wait set for that condition. The thread is not made runnable when the lock is available. Instead, it stays deactivated until another thread has called the signalAll method on the same condition.

When another thread has transferred money, it should call

sufficientFunds.signalAll();

This call reactivates all threads waiting for the condition. When the threads are removed from the wait set, they are again runnable, and the scheduler will eventually activate them again. At that time, they will attempt to re-enter the object. As soon as the lock is available, one of them will acquire the lock and continue where it left off, returning from the call to await.

At this time, the thread should test the condition again because there is no guarantee that the condition is now fulfilled—the signalAll method merely signals to the waiting threads that the condition may be fulfilled at this time and that it is worth checking for the condition again.

In general, a call to await should be inside a loop of the following form:

while (!(OK to proceed))
   condition.await();

It is crucially important that some other thread calls the signalAll method eventually. When a thread calls await, that thread has no way of reactivating itself. It puts its faith in the other threads. If none of them bother to reactivate the waiting thread, it will never run again. This can lead to unpleasant deadlock situations. If all other threads are blocked and the last active thread calls await without unblocking one of the others, it also blocks. No thread is left to unblock the others, and the program hangs.

The rule of thumb is to call signalAll whenever the state of an object changes in a way that might be advantageous to waiting threads. For example, whenever an account balance changes, the waiting threads should be given another chance to inspect the balance. For example, the following code calls signalAll when you have finished the funds transfer:

public void transfer(int from, int to, int amount)
{
   bankLock.lock(); try
   {
      While (accounts[from] < amount)
         sufficientFunds.await();
      // transfer funds
      ...
      sufficientFunds.signalAll();
   }
   finally
   {
      bankLock.unlock();
   }
}

Note that the call to signalAll does not immediately activate a waiting thread. The call only unblocks the waiting threads so that they can compete for entry into the object after the current thread has relinquished the lock.

Another method, signal, unblocks only a single thread from the wait set, chosen at random. That is more efficient than unblocking all threads, but there is a danger: If the randomly chosen thread finds that it still cannot proceed, it becomes blocked again. If no other thread calls signal again, the system deadlocks.

Note that a thread can call await, signalAll, or signal on a condition only if it owns the lock of the condition.

If you run the sample program in Listing 3, you will notice that nothing ever goes wrong: The total balance stays at $100,000 forever. No account ever has a negative balance. (Press Ctrl+C to terminate the program.) You may also notice that the program runs a bit slower—that is the price you pay for the added bookkeeping involved in the synchronization mechanism.

Listing 3. synch/Bank.java

package synch;

import java.util.*;
import java.util.concurrent.locks.*;

/**
 * A bank with a number of bank accounts that uses locks for serializing access.
 */

public class Bank
{
   private final double[] accounts;
   private Lock bankLock;
   private Condition sufficientFunds;

   /**
    * Constructs the bank.
    * @param n the number of accounts
    * @param initialBalance the initial balance for each account
    */

   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
      bankLock = new ReentrantLock();
      sufficientFunds = bankLock.newCondition();
   }

   /**
    * Transfers money from one account to another.
    * @param from the account to transfer from
    * @param to the account to transfer to
    * @param amount the amount to transfer
    */

   public void transfer(int from, int to, double amount) throws InterruptedException
   {
      bankLock.lock();
      try
      {
         while (accounts[from] < amount)
            sufficientFunds.await();
         System.out.print(Thread.currentThread());
         accounts[from] -= amount;
         System.out.printf(" %10.2f from %d to %d", amount, from, to);
         accounts[to] += amount;
         System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
         sufficientFunds.signalAll();
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * Gets the sum of all account balances.
    * @return the total balance
    */

   public double getTotalBalance()
   {
      bankLock.lock();
      try
      {
         double sum = 0;
         for (double a : accounts)
            sum += a;
            return sum;
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * Gets the number of accounts in the bank.
    * @return the number of accounts
    */

   public int size()
   {
      return accounts.length;
   }
}

Source: oracle.com

Friday, March 18, 2022

JobRunr 4.0.0: Static methods, caching, and performance analysis

JobRunr, Performance Analysis, Oracle Java Certification, Java Exam Prep, Oracle Java Learning, Oracle Java Preparation

The open source job scheduler for Java has significant updates that improve its performance and framework integrations.

JobRunr, an open source job scheduler library for Java, has significant updates that increase its performance and framework integrations.

You can use any existing method of Spring services to create a job without the need to implement an interface. A job can be a short- or long-running process, and it will be automatically offloaded to a background thread so that the current web request is not blocked.

A major update appeared late last year with JobRunr 4.0.0.

Job analysis performance mode. When a job is analyzed for the first time, JobRunr checks whether it can be cached. If so, all subsequent calls will be a lot faster. If the job is not cacheable, this is displayed in the dashboard.

JobRequest and JobRequestHandler. These features let you create jobs using the command pattern and the command handler pattern. Thus, you can use objects instead of lambdas to create background jobs. More about this below.

Static method support. JobRunr can now create jobs using lambdas that reference static methods.

CPU allocation irregularities detection. JobRunr uses the network time protocol (NTP) and timeouts to detect if a BackgroundJobServer is still alive. This can create a problem if the JVM is spending too much time doing garbage collection or if a cloud service stalls out due to excessive resources being used by other processes. JobRunr now detects the stall and shows a warning in the dashboard because it can impact the cluster.

There are also enhancements to the integrations with Spring Boot, Micronaut, and Quarkus. Therefore, the order of parameters passed to JobRunr has changed. In the earlier versions, covered in the 2021 article, the lambda was the first specified parameter. As of JobRunr 4.0.0, the lambda is the last parameter in the list.

Using JobRequest and JobRequestHandler

A JobRequest follows the command and the command handler patterns. The code below adds a background job to the job queue using a JobRequest. The JobRequest can contain data; when the job is started, the JobRequest object will be provided to the run method of the JobRequestHandler.

public class MyJobRequest implements JobRequest {

  private UUID id;

  public MyJobRequest(UUID id) {

    this.id = id;

  }

  @Override

  public Class<MyJobRequestHandler> getJobRequestHandler() {

      return MyJobRequestHandler.class;

  }

  public UUID getId() {

    return id;

  }

}

JobId jobId = BackgroundJobRequest.enqueue(new MyJobRequest(id));

When you use a JobRequest to create jobs, note that the JobRequest itself is nothing more than a data transfer object; you should not pass services or beans with it. The smaller the JobRequest is, the better, because it will be serialized to JSON and stored in the StorageProvider.

Note that the JobRequest will be serialized and deserialized going to and from JSON. This also means it needs a default no-argument constructor and all fields must be capable of being serialized and deserialized.

A JobRequestHandler is a regular service—such as a Spring bean, a Micronaut singleton, or a Quarkus singleton—where you can inject other services, and it must be resolvable by the inversion of control (IoC) container.

When the job will be invoked, JobRunr asks the IoC container for the relevant JobRequestHandler, calls the run method of the instance, and passes the JobRequest as an argument. You can use all the data from the JobRequest inside the JobRequestHandler, as needed.

For example, the following JobRequestHandler handles all MyJobRequest calls. Because it is a regular bean, you can inject other services.

@Component

public class MyJobRequestHandler implements JobRequestHandler<MyJobRequest> {

  @Inject

  private SomeService someService; // you can inject other services (or constructor-injection)

  @Override

  @Job(name = "Some neat Job Display Name", retries = 2)

  public void run(MyJobRequest jobRequest) {

      // do your background work here

  }

}

Source: oracle.com