How to protect shared data when it is accessed by multiple threads?
To protect the variables from shared access, we use volatile and AtomicReference(s). If it is a method or a block of code, then we use Synchronized or explicit locking. Apart from these, ThreadLocal is another way to protect the shared data where we distribute the copy of the shared variable to each thread. This post provides more details about each approach with a working example.
Volatile
Volatile variables makes the reads and writes to the main memory. But, it doesn't give synchronous behavior. To get the synchronous behavior, it has to be mixed with synchronized while performing read and writes.
Atomic variables support compound operations like increment, decrement on variables in an atomic or synchronous manner. For only such concurrent operations supported by the java atomic API, we can use them as an alternative for synchronization since they are fast and non-blocking.
Synchronized
- It protects methods/code blocks from concurrent access.
- Synchronized Method or Synchronized block ensures that the code inside it will be accessed by a single thread at any point of time.
Lock Interface
- The functionality of Lock Interface and Synchronized is same except that Lock interface provides more control on lock acquisition when compared to Synchronized block. Below are the different variants of lock acquisition supported.
- Syntax-wise, In the synchronized approach, locking, unlocking happens implicitly whereas, in this approach, they have to be set explicitly.
- Lock Fairness - Longest waiting thread gets the lock.
- Lock Interruptibility - Ability to interrupt thread while waiting for the lock. Useful in the scenarios where a thread is waiting for the lock for a very long time
- Lock timeout - Ability to timeout while waiting for the lock without blocking for a long time
- tryLock() - Try for lock without blocking
Thread Local
- ThreadLocal provides thread safety to the share reference not by restricting multiple thread access on the reference, instead, it creates a copy of the shared reference for each thread. so, each thread uses its own copy which cannot be shared with the other thread. Since, each thread has its own copy mutiple access on the shared variable will not have any impact
- We can wrap any thread-unsafe object in ThreadLocal to protect that object from thread safety
- ThreadLocal variables in Java are generally private static mutable fields in Classes. They are used in scenarios where per-thread context instances like UserContext, TransactionContext etc., has to be created.
public class ThreadLocalExample { public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j< 2; j++) { System.out.println(Thread.currentThread().getName() + " : " + DateFormatter.getDateFormatter().format(new Date())); } }, "THREAD-"+i).start(); } } } class DateFormatter { static boolean flip = false; private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { System.out.println("Creating SimpleDateFormat for Thread : "+ Thread.currentThread().getName()); if(flip()) return new SimpleDateFormat("dd/MM/yyyy"); else return new SimpleDateFormat("dd-MM-yyyy"); } }; public static DateFormat getDateFormatter(){ return dateFormatHolder.get(); } private static boolean flip() { return flip =(flip==false)?true:false; } } Output Creating SimpleDateFormat for Thread : THREAD-0 Creating SimpleDateFormat for Thread : THREAD-1 THREAD-1 : 28-07-2018 THREAD-0 : 28/07/2018 THREAD-0 : 28/07/2018 THREAD-1 : 28-07-2018
Comments
Post a Comment