Tuesday, September 18, 2012

Thread concepts

My learnings..

1. What is the difference between processes and threads?

A process is an execution of a program but a thread is a single execution sequence within the process. A process can contain multiple threads. A thread is sometimes called a lightweight process.


A JVM runs in a single process and threads in a JVM share the heap belonging to that process. That is why several threads may access the same object. Threads share the heap and have their own stack space. This is how one thread’s invocation of a method and its local variables are kept thread safe from other threads. But the heap is not thread-safe and must be synchronized for thread safety.
2. Briefly explain high-level thread states?
The state chart diagram below describes the thread states.

  • Runnable — A thread becomes runnable when you call the start( ), but does  not necessarily start running immediately.  It will be pooled waiting for its turn to be picked for execution by the thread scheduler based on thread priorities.

    ?
    1
    2
    MyThread aThread = new MyThread();
    aThread.start();                   //becomes runnable
  • Running: The processor is actively executing the thread code. It runs until it becomes blocked, or voluntarily gives up its turn with this static method Thread.yield( ). Because of context switching overhead, yield( ) should not be used very frequently
  • Waiting: A thread is in a blocked state while it waits for some external processing such as file I/O to finish.A call to currObject.wait( ) method causes the current thread to wait until some other thread invokes currObject.notify( ) or the currObject.notifyAll( ) is executed.
  • Sleeping: Java threads are forcibly put to sleep (suspended) with this overloaded method: Thread.sleep(milliseconds), Thread.sleep(milliseconds, nanoseconds);
  • Blocked on I/O: Will move to runnable after I/O condition like reading bytes of data etc changes.
  • Blocked on synchronization: will move to running when a lock is acquired. 
  • Dead: The thread is finished working.
Thread.State enumeration contains the possible states of a Java thread in the underlying JVM. These thread states are possible due to Java's following thread concepts:
  • The objects can be shared and modified (i.e. if mutable) by any threads.
  • The preemptive nature of the thread scheduler can swap threads on and off cores in a multi-core CPU machine at any time.
  • This means the methods can be swapped out while they are running. Otherwise a method in an infinite loop will clog the CPU forever leaving the other methods on different threads to starve. 
  • To prevent thread safety issues, the methods and block of code that has vulnerable data can be locked
  • This enables the threads to be in locked or waiting to acquire a lock states. 
  • The threads also get into the waiting state for I/O resources like sockets, file handles, and database connections. 
  • The threads that are performing I/O read/write operations can not be swapped. Hence, they need to either complete to the finished state with success/failure or another thread must close the socket for it to get to the state of dead or finished. This is why proper service timeout values are necessary to prevent the thread to get blocked for ever in an I/O operation, causing performance issues. 
  • The threads can be put to sleep to give other threads in waiting state an opportunity to execute.
3. Why is locking of a method or block of code for thread safety is called "synchronized" and not "lock" or "locked"?

When a method or block of code is locked with the reserved "synchronized" key word in Java, the memory (i.e. heap) where the shared data is kept is synchronized. This means,

  • When a synchronized block or method is entered after the lock has been acquired by a thread, it first reads any changes to the locked object from the main heap memory to ensure that the thread that has the lock has the current info before start executing.
  • After the synchronized  block has completed and the thread is ready to relinquish the lock, all the changes that were made to the object that was locked is written or flushed back to the main heap memory so that the other threads that acquire the lock next has the current info.

This is why it is called "synchronized" and not "locked". This is also the reason why the immutable objects are inherently thread-safe and does not require any synchronization. Once created, the immutable objects cannot be modified.

4. How does thread synchronization occurs inside a monitor? What levels of synchronization can you apply? What is the difference between synchronized method and synchronized block?

A.
In Java programming, each object has a lock. A thread can acquire the lock for an object by using the synchronized keyword. The synchronized keyword can be applied in method level (coarse grained lock – can affect performance adversely) or block level of code (fine grained lock). Often using a lock on a method level is too coarse. Why lock up a piece of code that does not access any shared resources by locking up an entire method. Since each object has a lock, dummy objects can be created to implement block level synchronization. The block level is more efficient because it does not lock the whole method.



The JVM uses locks in conjunction with monitors. A monitor is basically a guardian who watches over a sequence of synchronized code and making sure only one thread at a time executes a synchronized piece of code. Each monitor is associated with an object reference. When a thread arrives at the first instruction in a block of code it must obtain a lock on the referenced object. The thread is not allowed to execute the code until it obtains the lock. Once it has obtained the lock, the thread enters the block of protected code. When the thread leaves the block, no matter how it leaves the block, it releases the lock on the associated object. For static methods, you acquire a class level lock.


What is a daemon thread? 

A.
Daemon threads are sometimes called "service" or “background” threads. These are threads that normally run at a low priority and provide a basic service to a program when activity on a machine is reduced. An example of a daemon thread that is continuously running is the garbage collector thread. The JVM exits whenever all non-daemon threads have completed, which means that all daemon threads are automatically stopped. To make a thread  as a daemon thread in Java

 
myThread.setDaemon(true);

The JVM always has a main thread as default. The main thread is always non-daemon. The user threads are created from the main thread, and by default they are non-daemon. If you want to make a user created thread to be daemon (i.e. stops when the main thread stops), use the setDaemon(true) as shown above.
Why wait, notify, and notifyall methods are defined in the Object class, and not in the Thread class?

A.
Every Java Object has a monitor associated with it. The threads using that object can lock or unlock the monitor associated with the object.Wait and notify/notifyAll methods are responsible for acquiring and relinquishing the lock associated with the particular object. Calling wait causes the current thread to wait to acquire the lock of the Object, and calling notify/notifyAll relinquishes the lock and notify the threads waiting for that lock.

What does join( ) method do?

A.
t.join( ) allows the current thread to wait indefinitely until thread “t” is finished.  t.join (5000) allows the current thread to wait  for thread “t” to finish but does not wait longer than 5 seconds.

?
1
2
3
4
5
6
7
8
9
try {
  t.join(5000); //current thread waits for thread “t” to complete but does not wait more than 5 sec
  if(t.isAlive()){
     //timeout occurred. Thread “t” has not finished
  }
  else {
     //thread “t” has finished
  }  
}

For example, say you need to spawn multiple threads to do the work, and continue to the next step only after all of them have completed, you will need to tell the main thread to wait. this is done with thread.join() method.




Here is the RunnableTask. The task here is nothing but sleeping for 10 seconds as if some task is being performed. It also prints the thread name and timestamp as to when this task had started

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Date;
  
public class RunnableTask implements Runnable {
  
 @Override
 public void run() {
  Thread thread = Thread.currentThread();
  System.out.println(thread.getName() + " at " + new Date());
  try {
   Thread.sleep(10000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
  
}


The taskmanager manages the tasks by spawing multiple user threads from the main thread. The main thread is always created by default. The user threads 1-3 are run sequentially, i.e. thread-2 starts only after thread-1 completes, and so on. The user threads 4-6 start and executes concurrently.




?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TaskManager {
   
   
 public static void main(String[] args) throws InterruptedException {
  
  RunnableTask task = new RunnableTask();
  
    
  //threads 1-3 are run sequentially
  Thread thread1 = new Thread(task, "Thread-1");
  Thread thread2 = new Thread(task, "Thread-2");
  Thread thread3 = new Thread(task, "Thread-3");
    
  
  thread1.start(); //invokes run() on RunnableTask
  thread1.join();  // main thread blocks (for 10 seconds)
  thread2.start(); //invokes run() on RunnableTask
  thread2.join();  // main thread blocks (for 10 seconds)
  thread3.start(); //invokes run() on RunnableTask
  thread3.join();  // main thread blocks (for 10 seconds)
    
  
  Thread thread4 = new Thread(task, "Thread-4");
  Thread thread5 = new Thread(task, "Thread-5");
  Thread thread6 = new Thread(task, "Thread-6");
   
  
  thread4.start(); //invokes run() on RunnableTask
  thread5.start(); //invokes run() on RunnableTask
  thread6.start(); //invokes run() on RunnableTask
  
  
 }
  
  
}


Notice the times of the output. There is a 10 second difference bewteen threads 1-3. But Threads 4-6 started pretty much the same time.

1
2
3
4
5
6
Thread-1 at Fri Mar 02 16:59:22 EST 2012
Thread-2 at Fri Mar 02 16:59:32 EST 2012
Thread-3 at Fri Mar 02 16:59:42 EST 2012
Thread-4 at Fri Mar 02 16:59:47 EST 2012
Thread-6 at Fri Mar 02 16:59:47 EST 2012
Thread-5 at Fri Mar 02 16:59:47 EST 2012
 
If 2 different threads hit 2 different synchronized methods in an object at the same time will they both continue? 
A. No. Only one thread can acquire the lock in a synchronized method of an object. Each object has a synchronization lock. No 2 synchronized methods within an object can run at the same time. One synchronized method should wait for the other synchronized method to release the lock.   This is demonstrated here with method level lock. Same concept is applicable for block level locks as well.



If you have a circular reference of objects, but you no longer reference it from an execution thread, will this object be a potential candidate for garbage collection?
A. Yes. Refer diagram below.


 
Q. What are some of the threads related problems and what causes those problems?
A. DeadLock, LiveLock, and Starvation.

Deadlock occurs when two or more threads are blocked forever, waiting for each other. This may occur when two threads, each having a lock on the same resource, attempt to acquire a lock on the other's resource. Each thread would wait indefinitely for the other resource to release the lock, unless one of the user processes is terminated. The thread deadlock can occur in conditions such as:

  •  two threads calling Thread.join() on each other.
  •  two threads use nested synchronized blocks to lock two objects and blocks lock the same objects in different order.
What are some of the threads related problems and what causes those problems?
A. DeadLock, LiveLock, and Starvation.

Deadlock occurs when two or more threads are blocked forever, waiting for each other. This may occur when two threads, each having a lock on the same resource, attempt to acquire a lock on the other's resource. Each thread would wait indefinitely for the other resource to release the lock, unless one of the user processes is terminated. The thread deadlock can occur in conditions such as:

  •  two threads calling Thread.join() on each other.
  •  two threads use nested synchronized blocks to lock two objects and blocks lock the same objects in different order.

Starvation and livelock are much less common a problem than deadlock, and it occurs when all threads are blocked, or are otherwise unable to proceed due to unavailability of required resources, and the non-existence of any unblocked thread to make those resources available.

The thread livelock can occur in conditions such as:

  • all the threads in a program are stuck in infinite loops.
  • all the threads in a program execute Object.wait(0) on an object with zero parameter. The program is live-locked and cannot proceed until one or more threads call Object.notify( ) or Object.notifyAll() on the relevant objects.  Because all the threads are blocked, neither call can be made.

Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by "greedy" threads. For example, suppose an object provides a synchronized method that often takes a long time to return. If one thread invokes this method frequently, other threads that also need frequent synchronized access to the same object will often be blocked. The thread starvation can occur in conditions such as:

  • one thread cannot access the CPU because one or more other threads are monopolizing the CPU.
  • setting thread priorities inappropriately. A lower-priority thread can be starved by higher-priority threads if the higher-priority threads do not yield control of the CPU from time to time. 
 What happens if you restart a thread that has already started?
A. You will get the following exception

1
2
3
Exception in thread "main" java.lang.<B>IllegalThreadStateException</B>
 at java.lang.Thread.start(Thread.java:595)
 at deadlock.DeadlockTest.main(DeadlockTest.java:38

Some of the Java standard library classes like SimpleDateFormat is not thread-safe. Always check the API to see if a particular class is thread-safe. If a particular class or library is not therad-safe, you could do one of three things.

public class DateFormatTest {
  
  //anonymous inner class. Each thread will have its own copy
  private final static ThreadLocal<SimpleDateFormat> shortDateFormat =  new ThreadLocal<SimpleDateFormat>() {
            protected SimpleDateFormat initialValue() {
                 return new SimpleDateFormat("dd/MM/yyyy");
             }
  };
     
        
 public Date convert(String strDate)
                     throws ParseException {
   
    //get the SimpleDateFormat instance for this thread and parse the date string  
    Date d = shortDateFormat.get().parse(strDate);
    return d;
  }
 
Q. Can you have a true singleton class in Java? How would you write a thread-safe singleton class?
A. A singleton class is something for which only one instance exists per class loader. Single instance for a whole application cannot be guaranteed. That is just definition of what a singleton is. The one that is  popular with the interviewers is writing a thread-safe singleton class. For example, the following singleton class is not thread-safe because before a thread creates the Singleton instance, another thread can proceed to the instantiation part of the code -- instance = new Object( );  to create more than one instance of the Singleton object. Even though the code --> instance = new Object( ); appears to be single line, the JVM has to execute a number of internal steps like allocating memory, creating a new object and assigning the newly created object to the referenced variable. Only after the completion of these steps, the condition instance == null will return false.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//final so that cannot be subclassed
public final class Singleton {
   
    private static Object instance = null;
   
    //private so that it cannot be instantiated from outside this class
    private Singleton() {}
   
    public static Object getInstance() {
        if (instance == null) {
            instance = new Object(); 
        }
   
        return instance;
    }
}


So, you can make the above code thread-safe in a number of ways.


Option 1: Synchronize the whole method or the block of code. This approach is not efficient as the use of synchronized keyword in a singleton class means that only one thread will be executing the synchronized block at a time and all other threads would be waiting.


Option 2: Eagerly initialize the singleton instance when the class is actually loaded as opposed to initializing it lazily at at run time only when it is accessed.


?
1
2
3
4
5
6
7
8
9
10
11
12
13
//final so that cannot be subclassed
public final class ThreadSafeSingleton {
   
    //eager initialization and instantitiated as soon as the class is loaded by a classloader in the JVM 
    private static Object instance = new Object();
   
    //private so that it cannot be instantiated from outside this class
    private Singleton() {}
   
    public static Object getInstance() { 
        return instance;
    }
}

Option 3: You can use the "Initialize-On-Demand Holder Class" idiom proposed by Brian Goetz to create a thread-safe lazy-initialized Singleton as shown below by creating an inner class.


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class ThreadSafeSingleton {
   
    //private so that it cannot be instantiated from outside this class
    private ThreadSafeSingleton() {}
      
    //static inner class, invoked only when ThreadSafeSingleton.getInstance() is called
    private static class ThreadSafeSingletonHolder {
        private static ThreadSafeSingleton instance = new ThreadSafeSingleton();
    }
   
    public static Object getInstance() { 
        return ThreadSafeSingletonHolder.instance;
    }
}

Option 4: is to create a per thread singleton as discussed earlier with the ThreadLocal class for the SimpledateFormat.


 

No comments:

Post a Comment