When you implement a concurrent application that has one or more objects shared by several threads, you have to protect the access to their attributes using a synchronization mechanism as locks or the synchronized keyword to avoid data inconsistency errors.
These mechanisms have the following problems:
However, there is also an efficient alternative ways to primitive data types that avoid using synchronized keyword. In this post, we'll see how we can synchronize primitive data types in a efficient way.
To provide a better performance to this situation, the compare-and-swap operation was developed. This operation implements the modification of the value of a variable in the following three steps:
Java implements this mechanism in the atomic variables. These variables provide the compareAndSet() method that is an implementation of the compare-and-swap operation and other methods based on it. Java also introduced atomic arrays that provide atomic operations for arrays of integer or long numbers. In this post, you will learn how to use the AtomicIntegerArray class to work with atomic arrays.
The java.util.concurrent package has two sub-packages:
Atomic Variables
Atomic variables were introduced in Java Version 5 to provide atomic operations on single variables. When you work with a normal variable, each operation that you implement in Java is transformed in several instructions that is understandable by the machine when you compile the program. For example, when you assign a value to a variable, you only use one instruction in Java, but when you compile this program, this instruction is transformed in various instructions in the JVM language. This fact can provide data inconsistency errors when you work with multiple threads that share a variable.
To avoid these problems, Java introduced the atomic variables. When a thread is doing an operation with an atomic variable, if other threads want to do an operation with the same variable, the implementation of the class includes a mechanism to check that the operation is done in one step. Basically, the operation gets the value of the variable, changes the value in a local variable, and then tries to change the old value for the new one. If the old value is still the same, it does the change. If not, the method begins the operation again. This operation is called Compare and Set.
Atomic variables don't use locks or other synchronization mechanisms to protect the access to their values. All their operations are based on the Compare and Set operation. It's guaranteed that several threads can work with an atomic variable at a time without generating data inconsistency errors and its performance is better than using a normal variable protected by a synchronization mechanism.
Here is a list of some of the classes in this package and their short description:
Only AtomicInteger and AtomicLong extend from Number class but not AtomicBoolean. All other classes in the java.util.concurrent.atomic subpackage inherit directly from the Object class. Here we'll discuss AtomicInteger and the same thing apply to rest the of the classes.
In Java the integer primitive increment operation is not an atomic operation. When an integer is ncremented, the following logical steps are performed by the JVM
Each one of the aforementioned steps occurs in the JVM. The danger is that if you have multiple threads that all try to increment the same value, there is a chance that two or more of the threads will get the same value.
Each of these atomic classes provides methods to perform common operations, but each one is ensured to be performed as a single atomic operation. For example, rather than incrementing an integer using the standard increment operator.
You can ensure that the
is all accomplished without fear of another thread interrupting your operation by writing you code as follows.
AtomicInteger ai = new AtomicInteger(0);
int n = ai.incrementAndGet();
AtomicInteger
Important Methods in the AtomicInteger Class which are self explanatory and you don't have remember it's description. Have a look !
Let's try out an example to understand AtomicInteger or AtomicLong.
Suppose you have a counter value that is public and accessible by all threads. How do you update or access this common counter value safely without introducing the data race problem?
Simplest way is to synchronized keyword to ensure that the critical section is accessed by only one thread at a given point in time. The critical section will be very small, as in
However, this code is inefficient since it acquires and releases the lock every time just to increment the value of count. Alternatively, if you declare count as AtomicInteger or AtomicLong (whichever is suitable), then there is no need to use a lock with synchronized keyword
In this example, I demonstrate how incrementing normal integer and incrementing "atomic" interger are different and which is thread safe.Incrementing a shared Integer object without in a data race; however, incrementing a shared AtomicInteger will not result in a data race.
Sample Output
Increment value of atomicInteger : 1
Increment value of integer : 1
Increment value of integer : 1
Increment value of integer : 1
Increment value of atomicInteger : 2
Increment value of atomicInteger : 3
Have you noticed in the above output that Integer object is resulted in Race Condition, the final value of integer after incrementing 3 times is 1. For atomicInteger, however, it is 3.
Related Post
Class Loader concept in detail
How Thread exchange data in Java
If you know anyone who has started learning Java, why not help them out! Just share this post with them. Thanks for studying today!...
These mechanisms have the following problems:
- Deadlock: This situation occurs when a thread is blocked waiting for a lock that is locked by other threads and will never free it. This situation blocks the program, so it will never finish.
- If only one thread is accessing the shared object, it has to execute the code necessary to get and release the lock.
However, there is also an efficient alternative ways to primitive data types that avoid using synchronized keyword. In this post, we'll see how we can synchronize primitive data types in a efficient way.
To provide a better performance to this situation, the compare-and-swap operation was developed. This operation implements the modification of the value of a variable in the following three steps:
- You get the value of the variable, which is the old value of the variable.
- You change the value of the variable in a temporal variable, which is the new value of the variable.
- You substitute the old value with the new value, if the old value is equal to the actual value of the variable. The old value may be different from the actual value if another thread has changed the value of the variable.
Java implements this mechanism in the atomic variables. These variables provide the compareAndSet() method that is an implementation of the compare-and-swap operation and other methods based on it. Java also introduced atomic arrays that provide atomic operations for arrays of integer or long numbers. In this post, you will learn how to use the AtomicIntegerArray class to work with atomic arrays.
The java.util.concurrent package has two sub-packages:
- java.util.concurrent.atomic
- java.util.concurrent.locks
Atomic Variables
Atomic variables were introduced in Java Version 5 to provide atomic operations on single variables. When you work with a normal variable, each operation that you implement in Java is transformed in several instructions that is understandable by the machine when you compile the program. For example, when you assign a value to a variable, you only use one instruction in Java, but when you compile this program, this instruction is transformed in various instructions in the JVM language. This fact can provide data inconsistency errors when you work with multiple threads that share a variable.
To avoid these problems, Java introduced the atomic variables. When a thread is doing an operation with an atomic variable, if other threads want to do an operation with the same variable, the implementation of the class includes a mechanism to check that the operation is done in one step. Basically, the operation gets the value of the variable, changes the value in a local variable, and then tries to change the old value for the new one. If the old value is still the same, it does the change. If not, the method begins the operation again. This operation is called Compare and Set.
Atomic variables don't use locks or other synchronization mechanisms to protect the access to their values. All their operations are based on the Compare and Set operation. It's guaranteed that several threads can work with an atomic variable at a time without generating data inconsistency errors and its performance is better than using a normal variable protected by a synchronization mechanism.
Here is a list of some of the classes in this package and their short description:
Only AtomicInteger and AtomicLong extend from Number class but not AtomicBoolean. All other classes in the java.util.concurrent.atomic subpackage inherit directly from the Object class. Here we'll discuss AtomicInteger and the same thing apply to rest the of the classes.
In Java the integer primitive increment operation is not an atomic operation. When an integer is ncremented, the following logical steps are performed by the JVM
- Retrieve the value of the integer from memory
- Increment the value
- Assign the newly incremented value back to the appropriate memory location
- Return the value to the caller
So while we write the increment operator in a single line of Java code, such as:
int n = j++;Each one of the aforementioned steps occurs in the JVM. The danger is that if you have multiple threads that all try to increment the same value, there is a chance that two or more of the threads will get the same value.
Each of these atomic classes provides methods to perform common operations, but each one is ensured to be performed as a single atomic operation. For example, rather than incrementing an integer using the standard increment operator.
You can ensure that the
- get value,
- increment value,
- update memory, and
- assign the new value to n
is all accomplished without fear of another thread interrupting your operation by writing you code as follows.
AtomicInteger ai = new AtomicInteger(0);
int n = ai.incrementAndGet();
AtomicInteger
Important Methods in the AtomicInteger Class which are self explanatory and you don't have remember it's description. Have a look !
Let's try out an example to understand AtomicInteger or AtomicLong.
Suppose you have a counter value that is public and accessible by all threads. How do you update or access this common counter value safely without introducing the data race problem?
Simplest way is to synchronized keyword to ensure that the critical section is accessed by only one thread at a given point in time. The critical section will be very small, as in
However, this code is inefficient since it acquires and releases the lock every time just to increment the value of count. Alternatively, if you declare count as AtomicInteger or AtomicLong (whichever is suitable), then there is no need to use a lock with synchronized keyword
In this example, I demonstrate how incrementing normal integer and incrementing "atomic" interger are different and which is thread safe.Incrementing a shared Integer object without in a data race; however, incrementing a shared AtomicInteger will not result in a data race.
Sample Output
Increment value of atomicInteger : 1
Increment value of integer : 1
Increment value of integer : 1
Increment value of integer : 1
Increment value of atomicInteger : 2
Increment value of atomicInteger : 3
Related Post
Class Loader concept in detail
How Thread exchange data in Java
If you know anyone who has started learning Java, why not help them out! Just share this post with them. Thanks for studying today!...
No comments:
Post a Comment