Q1. What is monitor.
Ans. A monitor is like a building that contains one special room that can be occupied by only one thread at a time. The room usually contains some data. From the time a thread enters this room to the time it leaves, it has exclusive access to any data in the room. Entering the monitor building is called "entering the monitor." Entering the special room inside the building is called "acquiring the monitor." Leaving the room is called "releasing the monitor". Leaving the entire building is called "exiting the monitor".
In addition to being associated with a bit of data, a monitor is associated with one or more bits of code, which in this book will be called monitor regions. A monitor region is code that needs to be executed as one indivisible operation with respect to a particular monitor. In other words, one thread must be able to execute a monitor region from beginning to end without another thread concurrently executing a monitor region of the same monitor. A monitor enforces this one-thread-at-a-time execution of its monitor regions. The only way a thread can enter a monitor is by arriving at the beginning of one of the monitor regions associated with that monitor. The only way a thread can move forward and execute the monitor region is by acquiring the monitor.
Q2. Synchronization in java
Ans.
1. Synchronized methods (Intrinsic lock)
2. Synchronized blocks/Critical Sections (Intrinsic lock)
3. Explicit Locks (Extrinsic lock)
Ans. A monitor is like a building that contains one special room that can be occupied by only one thread at a time. The room usually contains some data. From the time a thread enters this room to the time it leaves, it has exclusive access to any data in the room. Entering the monitor building is called "entering the monitor." Entering the special room inside the building is called "acquiring the monitor." Leaving the room is called "releasing the monitor". Leaving the entire building is called "exiting the monitor".
In addition to being associated with a bit of data, a monitor is associated with one or more bits of code, which in this book will be called monitor regions. A monitor region is code that needs to be executed as one indivisible operation with respect to a particular monitor. In other words, one thread must be able to execute a monitor region from beginning to end without another thread concurrently executing a monitor region of the same monitor. A monitor enforces this one-thread-at-a-time execution of its monitor regions. The only way a thread can enter a monitor is by arriving at the beginning of one of the monitor regions associated with that monitor. The only way a thread can move forward and execute the monitor region is by acquiring the monitor.
Q2. Synchronization in java
Ans.
1. Synchronized methods (Intrinsic lock)
2. Synchronized blocks/Critical Sections (Intrinsic lock)
3. Explicit Locks (Extrinsic lock)
Synchronized method: If a task is in a call to one of the synchronized methods, all other tasks are blocked from entering any of the synchronized methods of that object until the first task returns from its call.
All objects automatically contain a single lock (also referred to as a monitor). When you call any synchronized method, that object is locked and no other synchronized method of that object can be called by another thread/task until the first thread finishes and releases the lock. Thus, there is a single lock that is shared by all the synchronized methods of a particular object, and this lock can be used to prevent object memory from being written by more than one task at a time.
NOTE: Synchronized keyword is not part of method signature and can be added while overriding.
All objects automatically contain a single lock (also referred to as a monitor). When you call any synchronized method, that object is locked and no other synchronized method of that object can be called by another thread/task until the first thread finishes and releases the lock. Thus, there is a single lock that is shared by all the synchronized methods of a particular object, and this lock can be used to prevent object memory from being written by more than one task at a time.
NOTE: Synchronized keyword is not part of method signature and can be added while overriding.
Synchronized blocks: It is similar to above, except that a section of code is synchronized instead of complete method. The section of code you want to isolate this way is called a critical section and is created using the synchronized keyword. Here, synchronized is used to specify the object whose lock is being used to synchronize the enclosed code. For ex:
synchronized(syncObject)
{
// This code can be accessed
}
This is called a synchronized block or critical section; before it can be entered, the lock must be acquired on syncObject. If some other task already has this lock, then the critical section cannot be entered until the lock is released.
Explicit Locks: The Java SE5 java.util.concurrent library also contains an explicit mutex mechanism. The Lock object must be explicitly created, locked and unlocked; thus, it produces less elegant code than the built-in synchronized form. However, it is more flexible for solving certain types of problems and may lead to performance improvement in complex situations. For ex:
public int next()
{
// Getting explicit lock object
lock.lock();
try
{
++currentEvenValue;
// Cause faster failure
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
finally
{
lock.unlock(); // Freeing the lock.
}
}
NOTE: If you use synchronized blocks or extrinsic locks, any object can be used for locking, thus making it possible for two objects to share the same lock as well as one object can have multiple locks. It doesn’t matter whether method being executed is static or non-static, since it only cares about lock in question.
NOTE: In case of explicit locks, it is important to internalize the idiom shown here:
Right after the call to lock(), you must place a try-finally statement with unlock() in the finally clause—this is the only way to guarantee that the lock is always released. Note that the return statement must occur inside the try clause to ensure that the unlock() doesn't happen too early and expose the data to a second task. With extrinsic lock objects, whole burden of obtaining and releasing lock objects lies to developer and not to JVM.In general, when you are using synchronized, there is less code to write, and the opportunity for user error is greatly reduced, so you’ll usually only use the explicit Lock objects when you’re solving special or complex problems. For example, with the synchronized keyword, you can’t try and fail to acquire a lock, or try to acquire a lock for a certain amount of time and then give up.
Q3. Locks in concurrency.
Ans. There are two types of locks – intrinsic locks and locks on objects
Intrinsic Object lock -> Lock on an object instance
public class MsLunch
{
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public class MsLunch
{
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized (lock1)
{
// Getting intrinsic lock here
if (condition is true)
c1++;
else
/* Here we call lock1.wait() instead of wait() i.e this.wait(). Later is
called in case of synchronized methods i.e when we have lock on calling
object itself. Similarly, we will use lock1.notify() in place of notify()
*/
/* Here we call lock1.wait() instead of wait() i.e this.wait(). Later is
called in case of synchronized methods i.e when we have lock on calling
object itself. Similarly, we will use lock1.notify() in place of notify()
*/
lock1.wait();
}
}
public void inc2()
{
synchronized(lock2)
{
c2++;
}
Q4. Explain re-entrant synchronization? }
}
}
Intrinsic Class lock -> Lock on an class Object i.e static methods
Class lock can be grabbed and released independently of object locks. If a non-static synchronized method calls a static synchronized method, it acquires both locks. Thus, only 1 thread can execute a static synchronized method and only 1 thread per instance of class can execute a non-static synchronized method. Any number of threads can execute non-synchronized methods.
Object Locks:
private final Lock lock = new ReentrantLock(); // Initializing a lock object
public boolean impendingBow(Friend bower)
{
Boolean myLock = false;
Boolean yourLock = false;
try
{
myLock = lock.tryLock();
yourLock = bower.lock.tryLock();
}
}
}
Ans. Recall that a thread cannot acquire a lock owned by another thread. But a thread can re-acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables re-entrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. Without re-entrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.
One task may acquire an object’s lock multiple times. This happens if one method calls a second method on the same object, which in turn calls another method on the same object, etc. JVM keeps track of the number of times the object has been locked. Each time the same task acquires another lock on the same object, the count is incremented and decremented each time task leaves a synchronized method. Naturally, multiple lock acquisition is only allowed for the task that acquired the lock in the first place. As the count goes to zero, lock is available for use by other tasks.
Q5. How do Re-entrant locks treat priority of threads.
Ans. Re-entrant lock issues locks on FCFS basis, thus overriding the thread priority (may not be a good idea, since developer had a reason behind assigning the priorities) and changing the desired scheduling behaviour. Hence, Reentrant lock should be used only if priorities and scheduling are not in concern.
Q6. Volatile keyword
One task may acquire an object’s lock multiple times. This happens if one method calls a second method on the same object, which in turn calls another method on the same object, etc. JVM keeps track of the number of times the object has been locked. Each time the same task acquires another lock on the same object, the count is incremented and decremented each time task leaves a synchronized method. Naturally, multiple lock acquisition is only allowed for the task that acquired the lock in the first place. As the count goes to zero, lock is available for use by other tasks.
Q5. How do Re-entrant locks treat priority of threads.
Ans. Re-entrant lock issues locks on FCFS basis, thus overriding the thread priority (may not be a good idea, since developer had a reason behind assigning the priorities) and changing the desired scheduling behaviour. Hence, Reentrant lock should be used only if priorities and scheduling are not in concern.
Q6. Volatile keyword
Ans. The volatile keyword is used on variables that may be modified simultaneously by other threads. This warns the compiler to fetch them fresh each time, i.e write them to RAM rather than caching them in registers. So, when thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable.
Similarly, when JVM enters a synchronized method/block, it reloads the data from main memory. And when JVM exits a synchronized method/block, it writes the data in cache to main memory.
This also inhibits certain optimizations that assume no other thread will change the values unexpectedly. Since other threads cannot see local variables, there is never any need to mark local variables volatile. You need synchronization to co-ordinate changes to variables from different threads, but often volatile will do just to look at them.
However, volatility on x++ is not guaranteed. volatile x ++ is treated as load x, add 1, store x. A second thread doing, x++ could interleave, causing x to be incremented only once.
Q7. Can a method be static and synchronized?
Ans. A static method can be synchronized. If you do so, the JVM will obtain a lock on the java.lang.Class instance associated with the object. It is similar to saying:
synchronized(XYZ.class) { ....}
Q8. Can we execute a static and non-static synchronized methods together.
Q8. Can we execute a static and non-static synchronized methods together.
Ans. As we know that a static method can only access static members of the class whereas a non-static method can access both static as well as non-static members. This liberty can put us into a situation where a non-static method might be updating some static data which a static method would also be changing.
If two different threads try to access the static and non-static synchronized methods concurrently, synchronization won't guarantee mutual exclusion. This is because non-static synchronized method requires a lock of the particular object and static synchronized method requires lock on the java.lang.Class object associated with the particular class. Both these locks have nothing to do with each other and they can be acquired by different threads at the same time and hence two different threads may execute a static and a non-static synchronized methods together.
It is certainly not advisable to allow such a thing to happen and hence the design of such a class needs to be re-checked. Otherwise the errors caused by simultaneous modification of the same static data by different threads may infuse some really-hard-to-detect bugs. Thus, we see that only declaring the methods as synchronized doesn't ensure mutual exclusion and the programmer should always think about the locks involved to achieve the actual purpose of synchronization.
Ans. No, code like this is not allowed. However, you can obtain lock on two objects as below:
If two different threads try to access the static and non-static synchronized methods concurrently, synchronization won't guarantee mutual exclusion. This is because non-static synchronized method requires a lock of the particular object and static synchronized method requires lock on the java.lang.Class object associated with the particular class. Both these locks have nothing to do with each other and they can be acquired by different threads at the same time and hence two different threads may execute a static and a non-static synchronized methods together.
It is certainly not advisable to allow such a thing to happen and hence the design of such a class needs to be re-checked. Otherwise the errors caused by simultaneous modification of the same static data by different threads may infuse some really-hard-to-detect bugs. Thus, we see that only declaring the methods as synchronized doesn't ensure mutual exclusion and the programmer should always think about the locks involved to achieve the actual purpose of synchronization.
Q9. Can I provide two locks in synchronized block like synchronized (lock1, lock2) { }
synchronized (lock1)
{
synchronized (lock2)
synchronized (lock2)
{
…...
wait();
}
}
NOTE: wait() releases the lock only on lock2 and it still holds on lock1. Thus, if someone else or your own program is waiting for lock1, it may result in deadlock. Thus, you should always avoid holding two locks at same time.
Q10. If a program fails in synchronized block or after obtaining the lock explicitly, will it be able to free the lock.
Ans. Locks associated with synchronized keyword are always released when thread leaves the scope of method/block, whether normally or via an Exception. However, if lock is obtained explicitly, JVM can’t figure out the scope of the lock and thus such lock is never free until a call to unlock() is made.
NOTE: JVM releasing locks on exception i.e. leaving synchronized block/method scope may not be beneficial always; since system/data could have been left in inconsistent state and its not preferred to proceed with erroneous state.
Q11. Give one example for using Lock objects.
Ans. This is useful for implementing specialized synchronization structures, such as hand-overhand locking (also called lock coupling), used for traversing the nodes of a linked list—the traversal code must capture the lock of the next node before it releases the current node’s lock.
}
NOTE: wait() releases the lock only on lock2 and it still holds on lock1. Thus, if someone else or your own program is waiting for lock1, it may result in deadlock. Thus, you should always avoid holding two locks at same time.
Q10. If a program fails in synchronized block or after obtaining the lock explicitly, will it be able to free the lock.
Ans. Locks associated with synchronized keyword are always released when thread leaves the scope of method/block, whether normally or via an Exception. However, if lock is obtained explicitly, JVM can’t figure out the scope of the lock and thus such lock is never free until a call to unlock() is made.
NOTE: JVM releasing locks on exception i.e. leaving synchronized block/method scope may not be beneficial always; since system/data could have been left in inconsistent state and its not preferred to proceed with erroneous state.
Q11. Give one example for using Lock objects.
Ans. This is useful for implementing specialized synchronization structures, such as hand-overhand locking (also called lock coupling), used for traversing the nodes of a linked list—the traversal code must capture the lock of the next node before it releases the current node’s lock.
Q12. Talk about Condition class and why would you need it.
Ans. When using synchronized keyword, notify(), notifyAll() and wait() are used for inter-process communication. However, when you are using Explicit lock objects, you need to make use of Condition class. It is a basic class that uses a mutex and allows task suspension.
A single Lock produces a Condition object which is used to manage inter-task communication. However, the Condition object contains no information about the state of the process, so you need to manage additional information to indicate process state (may be a boolean variable).
You can suspend a task by calling await() on a Condition. When external state changes take place that might mean that a task should continue processing, you notify the task by calling signal() - to wake up one task, or signalAll() - to wake up all tasks that have suspended themselves on that Condition object (as with notifyAll(), signalAll() is the safer approach). As with the built-in versions, a task must own the lock before it can call await(), signal() or signalAll(). For ex:
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
condition.signalAll(); // Similar to notifyAll()
condition.await(); // Similar to wait()
Q13. Can you use associate different notifications with one lock.
Ans. With implicit synchronization mechanism, its not possible of assigning a lock object to any notification object i.e. associate with some condition. Here, all threads waiting on one object are assumed to be waiting on same condition. However, with explicit lock objects, we can get two different condition objects with a single lock. This is useful in cases, when you need to protect shared data, but you need to send different notifications. For ex:
Q13. Can you use associate different notifications with one lock.
Ans. With implicit synchronization mechanism, its not possible of assigning a lock object to any notification object i.e. associate with some condition. Here, all threads waiting on one object are assumed to be waiting on same condition. However, with explicit lock objects, we can get two different condition objects with a single lock. This is useful in cases, when you need to protect shared data, but you need to send different notifications. For ex:
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Here both condition objects are bound with same lock, but each has its own wait and notify mechanism.
Here both condition objects are bound with same lock, but each has its own wait and notify mechanism.
Q14. How can you ensure visibility of operations across the application?
Ans. The volatile keyword ensures visibility across the application. If you declare a field to be volatile, this means that as soon as a write occurs for that field, all reads will see the change. This is true even if local caches are involved as volatile fields are immediately written through to main memory, and reads occur from main memory. Any writes that a task makes will be visible to that task, so you don’t need to make a field volatile if it is only seen within a task.
Ans. The volatile keyword ensures visibility across the application. If you declare a field to be volatile, this means that as soon as a write occurs for that field, all reads will see the change. This is true even if local caches are involved as volatile fields are immediately written through to main memory, and reads occur from main memory. Any writes that a task makes will be visible to that task, so you don’t need to make a field volatile if it is only seen within a task.
Atomicity and volatility are distinct concepts. An atomic operation on a non-volatile field will not necessarily be flushed to main memory, and so another task that reads that field will not necessarily see the new value. If multiple tasks are accessing a field, that field should be volatile; otherwise, the field should only be accessed via synchronization as Synchronization also causes flushing to main memory, so if a field is completely guarded by synchronized methods or blocks, it is not necessary to make it volatile.
NOTE: volatile doesn’t work when the value of a field depends on its previous value (such as incrementing a counter), nor does it work on fields whose values are constrained by the values of other fields, such as the lower and upper bound of a Range class which must obey the constraint lower <= upper.
It’s typically only safe to use volatile instead of synchronized if the class has only one mutable field. Again, your first choice should be to use the synchronized keyword—that’s the safest approach, and trying to do anything else is risky.
NOTE: Increment operation in Java is not an atomic operation. It involves reading the variable, adding 1 and putting back the value.
NOTE: volatile doesn’t work when the value of a field depends on its previous value (such as incrementing a counter), nor does it work on fields whose values are constrained by the values of other fields, such as the lower and upper bound of a Range class which must obey the constraint lower <= upper.
It’s typically only safe to use volatile instead of synchronized if the class has only one mutable field. Again, your first choice should be to use the synchronized keyword—that’s the safest approach, and trying to do anything else is risky.
NOTE: Increment operation in Java is not an atomic operation. It involves reading the variable, adding 1 and putting back the value.
No comments:
Post a Comment