Exception Handling Java Interview Questions – Set 07

Explain how you would get thread-safety issues due to non-atomic operations with a code example

The code snippets below demonstrates non-atomic operations producing incorrect results with code. The program below uses a shared Counter object, that is shared between three concurrent users (i.e. three threads). The Counter object is responsible for incrementing the counter.

Firstly, the Counter class. The counted values are stored in a HashMap by name (i.e. thread name) as the key for later retrieval.

import java.util.HashMap;

import java.util.Map;

public class Counter {

//         shared variable or resource

private Integer count = Integer.valueOf(0);

private Map<String, Integer> userToNumber = new HashMap<String, Integer>(10);

public void  increment() {

try {

count = count + 1;      //          increment the counter

Thread.sleep(50);      //          to imitate other operations and

//          to make the racing condion to occur more often for the demo

Thread thread = Thread.currentThread();

userToNumber.put(thread.getName(), count);

}

catch (InterruptedException e) {

e.printStackTrace();

}

}

public Integer getCount(String name) {

return userToNumber.get(name);

}

}

Next, the Runnable task where each thread will be entering and executing concurrently.

public class CountingTask implements Runnable

{

private Counter counter;

public CountingTask(Counter counter) {

super();

this.counter = counter;

}

public void run() {

counter.increment();

Thread thread = Thread.currentThread();

System.out.println(thread.getName() + ” value is ” + counter.getCount(thread.getName()));

}

}

Finally, the Manager class that creates 3 new threads from the main thread.

public class CountingManager {

public static void main(String[] args) throws InterruptedException

{

Counter counter = new Counter(); // create an instance of the Counter

CountingTask task = new CountingTask(counter); // pass the counter to the runnable CountingTask

//Create 10 user threads (non-daemon) from the main thread that share the counter object

Thread thread1 = new Thread(task, “User-1”);

Thread thread2 = new Thread(task, “User-2”);

Thread thread3 = new Thread(task, “User-3”);

//start the threads

thread1.start();

thread2.start();

thread3.start();

//observe the racing conditions in the output

}

}

To see the racing condition, inspect the output of the above code:

User-3 value is 3

User-1 value is 3

User-2 value is 3

All three threads or users get assigned the same value of 3 due to racing conditions. We are expecting to see three different count values to be assigned from 1 to 3. What happened here is that when the first thread incremented the count from 0 to 1 and entered into the sleep(50) block, the second and third threads incremented the counts from 1 to 2 and 2 to 3 respectively.

This shows that the 2 operations — the operation that increments the thread and the operation that stores the incremented value in a HashMap are not atomic, and produces incorrect results due to racing conditions.

Explain how you would get a Thread Deadlock with a code example

The example below causesa deadlocksituation bythread-1 waiting for lock2and thread-0 waiting for lock1.

 

package deadlock;

public class DeadlockTest extends Thread

{

public static Object lock1 = new Object();

public static Object lock2 = new Object();

public void method1()

{

synchronized (lock1)

{

delay(500);  //some operation

System.out.println(“method1: ” + Thread.currentThread().getName());

synchronized (lock2)

{

System.out.println(“method1 is executing …. “);

}

}

}

public void method2()

{

synchronized (lock2)

{

delay(500);   //some operation

System.out.println(“method1: ” + Thread.currentThread().getName());

synchronized (lock1)

{

System.out.println(“method2 is executing …. “);

}

}

}

public void run()

{

method1();

method2();

}

public static void main(String[] args)

{

DeadlockTest thread1 = new DeadlockTest();

DeadlockTest thread2 = new DeadlockTest();

thread1.start();

thread2.start();

}

private</span> void delay(long timeInMillis)

{

try

{

Thread.sleep(timeInMillis);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

}

The output will be something like:

method1: Thread-0

method1 is executing ….

method2: Thread-0

method1: Thread-1

—deadlock —– can’t proceed further

Can two threads call two different synchronized instance methods of an Object

No. If a object has synchronized instance methods then the Object itself is used a lock object for controlling the synchronization. Therefore all other instance methods need to wait until previous method call is completed. See the below sample code which demonstrate it very clearly. The Class Common has 2 methods called synchronizedMethod1() and synchronizedMethod2() MyThread class is calling both the methods

public class Common {

public synchronized void synchronizedMethod1() {

System.out.println(“synchronizedMethod1 called”);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“synchronizedMethod1 done”);

}

public synchronized void synchronizedMethod2() {

System.out.println(“synchronizedMethod2 called”);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“synchronizedMethod2 done”);

}

}

public class MyThread extends Thread {

private int id = 0;

private Common common;

public MyThread(String name, int no, Common object) {

super(name);

common = object;

id = no;

}

public void run() {

System.out.println(“Running Thread” + this.getName());

try {

if (id == 0) {

common.synchronizedMethod1();

} else {

common.synchronizedMethod2();

}

} catch (Exception e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

Common c = new Common();

MyThread t1 = new MyThread(“MyThread-1”, 0, c);

MyThread t2 = new MyThread(“MyThread-2”, 1, c);

t1.start();

t2.start();

}

}