Thread safety is avoiding thread interference.Thread interference is two or more threads executing a set of operations on the same data concurrently.This causes problem. To avoid this we should make sure that one thread runs at a time and not to allow another thread to run on the same data until it is finish.
This can be achieved in java by synchronization and from java1.5 there is a new way of doing this, that is Atomic actions also called as non blocking algorithms.
synchronization can be achieved in 2 ways. one is synchronizing the methods and the other is using synchronized blocks.
synchronizing methods:-
synchronized keyword can only be used in front of the methods. The following is the example.
Example:-
public class SynchMethEx {
private int counter;
public synchronized void increase(){
// do some operations and increse the counter
//to the desired value
}
public synchronized void decrease(){
// do some operations and decrease the counter
//to the desired value
}
public synchronized int getCounter(){
return counter;
}
}
in this when a thread enters into one synchronized method of the class it achieves the intrinsic lock of that object so that any other thread cannot enter any of the synchronized methods of that class until the thread comes out of the synchronized method it entered. Once the thread comes out of the synchronized method and before it enters into another synchronized the lock on that object will be released so there is a chance that another thread can attain the lock and perform a synchronized method.
synchronizing the whole method will be a bottleneck for performance. Instead only synchronize the block which needs thread safety.
synchronized blocks:-
The synchronized blocks improves the performance by only synchronizing the block of code which is necessary. The following is the example.
Example:-
public class SynchBlock {
public static void main(String[] args)throws Exception {
Thread b=new Thread(new Kir(ThreadSync.class),"2");
Thread c=new Thread(new Kir(ThreadSync.class),"3");
Thread d=new Thread(new Kir(ThreadSync.class),"4");
b.start();
c.start();
d.start();
}
private static class Kir implements Runnable{
private Object obj;
public Kir(Object obj){
this.obj=obj;
}
public void run(){
synchronized(obj){
int i=0;
while(true){
System.out.println("Thread"+Thread.currentThread().getName()+"@"+i);
if(i==100)break;
i++;
}//end of the while loop.
}//end of the synchronized block.
}//end of run method.
}//end of the inner class.
}//end of the main class.
Before the thread enters into the synchronized block it has to obtain intrinsic lock on the object specified in the in the parentheses of the block definition(in the above example the lock on the object it has to attain is “obj”). Once it achieves the lock it will be able to execute the synchronized block. Once the thread is out of the synchronized block the lock on that particular object will be released so that another thread can attain a lock and can execute the synchronized block.
so the above two methods achieve thread safety but there is a problem. when one thread attained intrinsic lock on the object, The other threads that try to attain lock on the same object move to the runnable state and has to be rescheduled and also need to compete for the lock with the other threads who are waiting for the lock on the same object.
So java1.5 came with a solution called Atomic actions or Non Blocking Algorithms.
Atomic Actions or Non Blocking Algorithms(Since java1.5):-
In concurrent programming an operation (or set of operations) is atomic, linearizable, indivisible or uninterruptible if it appears to the rest of the system to occur instantaneously.Atomicity is a guarantee of isolation from concurrent process. Additionally, atomic operations commonly have a succeed or fail definition — they either successfully change the state of the system, or have no apparent effect.
In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.
An atomic action has to happen all at once or none. It is not that thread which is executing the atomic operation need not be in a running state until it completes the action. The atomic action ensures that the effects of it or seen only after the complete execution and if this detects the thread interference it does nothing. It ensures thread safety. It appears like all happened at once
For example the following are the steps involved in atomic operation:-
Take the value from the shared variable.
increment the value by one
set the value back again to the shared variable.
so the operation has to look like the complete operation had happened at once or instantaneously. so it follows the following algorithm.
Take the value of the shared variable into another local variable(int i=j).
Add one to it and assign it to another variable(int k=i+1)
Now lock the shared variable(may be at hardware level) and check whether the shared variable still holds the value that it took at the beginning of the operation.
If it is the same then set the increased value(k) to the shared variable (j) and release the lock so that other threads can perform operation on shared variable.
If it is not the same (suppose while these operations are happening another thread got it’s turn and changed the shared variable value) then nothing happens, it does not change the shared variable value and also releases the lock making the variable available to other threads for operating on it.
So the whole operation looks like it happened at once or not happened at all.
This is how it avoids thread interference and ensures thread safety.
Atomicity is commonly enforced by mutual exclusion, whether at the hardware level building on a cache coherency protocol, or the software level using semaphores or locks. Thus, an atomic operation does not actually occur instantaneously. The benefit comes from the appearance: the system behaves as if each operation occurred instantly, separated by pauses.
In computer science, compare-and-swap (CAS) is an atomic instruction used in multithreading to achieve synchronization. It compares the contents of a memory location to a given value and, only if they are the same, modifies the contents of that memory location to a given new value. This is done as a single atomic operation. The atomicity guarantees that the new value is calculated based on up-to-date information; if the value had been updated by another thread in the meantime, the write would fail. The result of the operation must indicate whether it performed the substitution; this can be done either with a simple Boolean response (this variant is often called compare-and-set), or by returning the value read from the memory location (not the value written to it).
In java
Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
Reads and writes are atomic for all variables declared volatile (including long and double variables).
Since java1.5 Some of the classes in the package “java.util.concurrent” provide atomic methods that do not rely on synchronization.
Atomic variables:-
The “java.util.concurrent.atomic” package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables.
Example:-
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
private AtomicInteger c = new AtomicInteger(0);
public void increment() {
c.incrementAndGet(); //This is atomic operation. Internally uses CAS.
}
public void decrement() {
c.decrementAndGet(); // this is atomic. Internally uses CAS.
}
public int value() {
return c.get();
}
}
The Atomic variables also contain CAS methods which perform Atomic operations.
This can be achieved in java by synchronization and from java1.5 there is a new way of doing this, that is Atomic actions also called as non blocking algorithms.
synchronization can be achieved in 2 ways. one is synchronizing the methods and the other is using synchronized blocks.
synchronizing methods:-
synchronized keyword can only be used in front of the methods. The following is the example.
Example:-
public class SynchMethEx {
private int counter;
public synchronized void increase(){
// do some operations and increse the counter
//to the desired value
}
public synchronized void decrease(){
// do some operations and decrease the counter
//to the desired value
}
public synchronized int getCounter(){
return counter;
}
}
in this when a thread enters into one synchronized method of the class it achieves the intrinsic lock of that object so that any other thread cannot enter any of the synchronized methods of that class until the thread comes out of the synchronized method it entered. Once the thread comes out of the synchronized method and before it enters into another synchronized the lock on that object will be released so there is a chance that another thread can attain the lock and perform a synchronized method.
synchronizing the whole method will be a bottleneck for performance. Instead only synchronize the block which needs thread safety.
synchronized blocks:-
The synchronized blocks improves the performance by only synchronizing the block of code which is necessary. The following is the example.
Example:-
public class SynchBlock {
public static void main(String[] args)throws Exception {
Thread b=new Thread(new Kir(ThreadSync.class),"2");
Thread c=new Thread(new Kir(ThreadSync.class),"3");
Thread d=new Thread(new Kir(ThreadSync.class),"4");
b.start();
c.start();
d.start();
}
private static class Kir implements Runnable{
private Object obj;
public Kir(Object obj){
this.obj=obj;
}
public void run(){
synchronized(obj){
int i=0;
while(true){
System.out.println("Thread"+Thread.currentThread().getName()+"@"+i);
if(i==100)break;
i++;
}//end of the while loop.
}//end of the synchronized block.
}//end of run method.
}//end of the inner class.
}//end of the main class.
Before the thread enters into the synchronized block it has to obtain intrinsic lock on the object specified in the in the parentheses of the block definition(in the above example the lock on the object it has to attain is “obj”). Once it achieves the lock it will be able to execute the synchronized block. Once the thread is out of the synchronized block the lock on that particular object will be released so that another thread can attain a lock and can execute the synchronized block.
so the above two methods achieve thread safety but there is a problem. when one thread attained intrinsic lock on the object, The other threads that try to attain lock on the same object move to the runnable state and has to be rescheduled and also need to compete for the lock with the other threads who are waiting for the lock on the same object.
So java1.5 came with a solution called Atomic actions or Non Blocking Algorithms.
Atomic Actions or Non Blocking Algorithms(Since java1.5):-
In concurrent programming an operation (or set of operations) is atomic, linearizable, indivisible or uninterruptible if it appears to the rest of the system to occur instantaneously.Atomicity is a guarantee of isolation from concurrent process. Additionally, atomic operations commonly have a succeed or fail definition — they either successfully change the state of the system, or have no apparent effect.
In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.
An atomic action has to happen all at once or none. It is not that thread which is executing the atomic operation need not be in a running state until it completes the action. The atomic action ensures that the effects of it or seen only after the complete execution and if this detects the thread interference it does nothing. It ensures thread safety. It appears like all happened at once
For example the following are the steps involved in atomic operation:-
Take the value from the shared variable.
increment the value by one
set the value back again to the shared variable.
so the operation has to look like the complete operation had happened at once or instantaneously. so it follows the following algorithm.
Take the value of the shared variable into another local variable(int i=j).
Add one to it and assign it to another variable(int k=i+1)
Now lock the shared variable(may be at hardware level) and check whether the shared variable still holds the value that it took at the beginning of the operation.
If it is the same then set the increased value(k) to the shared variable (j) and release the lock so that other threads can perform operation on shared variable.
If it is not the same (suppose while these operations are happening another thread got it’s turn and changed the shared variable value) then nothing happens, it does not change the shared variable value and also releases the lock making the variable available to other threads for operating on it.
So the whole operation looks like it happened at once or not happened at all.
This is how it avoids thread interference and ensures thread safety.
Atomicity is commonly enforced by mutual exclusion, whether at the hardware level building on a cache coherency protocol, or the software level using semaphores or locks. Thus, an atomic operation does not actually occur instantaneously. The benefit comes from the appearance: the system behaves as if each operation occurred instantly, separated by pauses.
In computer science, compare-and-swap (CAS) is an atomic instruction used in multithreading to achieve synchronization. It compares the contents of a memory location to a given value and, only if they are the same, modifies the contents of that memory location to a given new value. This is done as a single atomic operation. The atomicity guarantees that the new value is calculated based on up-to-date information; if the value had been updated by another thread in the meantime, the write would fail. The result of the operation must indicate whether it performed the substitution; this can be done either with a simple Boolean response (this variant is often called compare-and-set), or by returning the value read from the memory location (not the value written to it).
In java
Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
Reads and writes are atomic for all variables declared volatile (including long and double variables).
Since java1.5 Some of the classes in the package “java.util.concurrent” provide atomic methods that do not rely on synchronization.
Atomic variables:-
The “java.util.concurrent.atomic” package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables.
Example:-
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
private AtomicInteger c = new AtomicInteger(0);
public void increment() {
c.incrementAndGet(); //This is atomic operation. Internally uses CAS.
}
public void decrement() {
c.decrementAndGet(); // this is atomic. Internally uses CAS.
}
public int value() {
return c.get();
}
}
The Atomic variables also contain CAS methods which perform Atomic operations.
No comments:
Post a Comment