Friday, August 12, 2011

Java Threads Notes

Here are some key notes I always wrote in thread notes txt file -

A process generally has a complete, private set of basic run-time resources; in particular, each process has its own memory space.

To facilitate communication between processes, most operating systems support Inter Process Communication (IPC) resources, such as pipes and sockets.

Java virtual machine run as a single process.

A Java application can create additional processes using a ProcessBuilder object

Threads exist within a process — every process has at least one. Threads share the process's resources, including memory and open files.

There are two basic strategies for using Thread objects to create a concurrent application.
To directly control thread creation and management, simply instantiate Thread each time the application needs to initiate an asynchronous task.
To abstract thread management from the rest of your application, pass the application's tasks to an executor.
There are two ways to do this:
Provide a Runnable object. The Runnable interface defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor, as in the HelloRunnable example:

public class HelloRunnable implements Runnable {

public void run() {
System.out.println("Hello from a thread!");
}

public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}

}

Subclass Thread. The Thread class itself implements Runnable, though its run method does nothing. An application can subclass Thread, providing its own implementation of run, as in the HelloThread example:

public class HelloThread extends Thread {

public void run() {
System.out.println("Hello from a thread!");
}

public static void main(String args[]) {
(new HelloThread()).start();
}

}
Notice that both examples invoke Thread.start in order to start the new thread.

Thread.sleep causes the current thread to suspend execution for a specified period.

public class SleepMessages {
public static void main(String args[]) throws InterruptedException {
for (int i = 0; i < 10; i++) {
//Pause for 4 seconds
Thread.sleep(4000);
//Print a message
System.out.println(i);
}
}
}

Notice that main declares that it throws InterruptedException. This is an exception that sleep throws when another thread interrupts the current thread while sleep is active.

Many methods that throw InterruptedException, such as sleep, join, wait.

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt

for (int i = 0; i < 10; i++) {
//Pause for 4 seconds
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
//We've been interrupted: no more messages.
return;
}
//Print a message
System.out.println(i);
}

What if a thread goes a long time without invoking a method that throws InterruptedException?
Then it must periodically invoke Thread.interrupted, which returns true if an interrupt has been received.

For example:

for (int i = 0; i < inputs.length; i++) {
heavyCrunch(inputs[i]);
if (Thread.interrupted()) {
//We've been interrupted: no more crunching.
return;
}
}

In this simple example, the code simply tests for the interrupt and exits the thread if one has been received.

The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so.

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,

t.join();

causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period.

Synchronized methods and synchronized statements.

public class SynchronizedCounter {
private int c = 0;

public synchronized void increment() {
c++;
}

public synchronized void decrement() {
c--;
}

public synchronized int value() {
return c;
}
}

If count is an instance of SynchronizedCounter, then making these methods synchronized has two effects:
First, it is not possible for two invocations of synchronized methods on the same object to interleave.
When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block, on any other synchronized method as well (suspend execution) until the first thread is done with the object.
Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.
Note that constructors cannot be synchronized — using the synchronized keyword with a constructor is a syntax error. In Java, synchronization of contructors is not allowed (results in a compile time error) as only the thread which is constructing the object should have access to the object and hence any other thread is not granted access until the construction of the object is complete. So, no
explicit synchronization needed for constructors.

(An important exception: final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed)
Every object has an intrinsic lock associated with it.

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock.
When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.
Another way to create synchronized code is with synchronized statements. Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:

public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}

Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables reentrant 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.

Volatile variables are treated different from other variables. When you mark a variable as volatile, JVM does not allow threads to cache volatile variables.The writes to volatile variables are guaranteed to be immediately visible to other threads. The side effect of this is- there is no way to render a read-modify-write sequence of operations atomic, meaning, for example, that a volatile variable cannot be used to reliably implement a mutex (mutual exclusion lock) or a counter.

Deadlock

public static Object cacheLock = new Object();
public static Object tableLock = new Object();
...
public void oneMethod() {
synchronized (cacheLock) {
synchronized (tableLock) {
doSomething();
}
}
}
public void anotherMethod() {
synchronized (tableLock) {
synchronized (cacheLock) {
doSomethingElse();
}
}
}

Conditions -
Mutual Exclusion: a resource that cannot be used by more than one process at a time
Hold and Wait: processes already holding resources may request new resources held by other processes
No Preemption: No resource can be forcibly removed from a process holding it, resources can be released only by the explicit action of the process.
Circular Wait: two or more processes form a circular chain where each process waits for a resource that the next process in the chain holds. When circular waiting is triggered by mutual exclusion operations it is sometimes called lock inversion.[2]
http://en.wikipedia.org/wiki/Deadlock#Prevention

No comments:

Post a Comment