What are the possible ways to make code thread-safe without using the synchronized keyword?
- 2why close ? Its a good succinct questionNimChimpsky– NimChimpsky2012-04-26 09:55:01 +00:00Commented Apr 26, 2012 at 9:55
- @NimChimpsky I don't know either, the question seems perfectly valid to me. It is definitely answerable, and the answers are not something subjective.Malcolm– Malcolm2012-04-26 10:00:59 +00:00Commented Apr 26, 2012 at 10:00
- @NimChimpsky "Too broad" I presume.OrangeDog– OrangeDog2018-08-03 11:31:22 +00:00Commented Aug 3, 2018 at 11:31
- To add to discussion I just posted a lengthy epilogue here Thread synchronization without locksmindiga– mindiga2021-06-25 10:19:17 +00:00Commented Jun 25, 2021 at 10:19
- I just posted the answer here. Synchronization without locksmindiga– mindiga2021-06-25 10:20:46 +00:00Commented Jun 25, 2021 at 10:20
7 Answers
Actually, lots of ways:
- No need for synchronization at all if you don't have mutable state.
- No need for synchronization if the mutable state is confined to a single thread. This can be done by using local variables or
java.lang.ThreadLocal. - You can also use built-in synchronizers.
java.util.concurrent.locks.ReentrantLockhas the same functionality as the lock you access when usingsynchronizedblocks and methods, and it is even more powerful.
4 Comments
synchronized than extrinsic locks if you don't need advanced features. The notaion of intrinsic locks is concise, and the code doesn't need to contain try-finally blocks (therefore much safer).You can make your code thread-safe by making all the data immutable, if there is no mutability, everything is thread-safe.
Secondly, you may want to have a look at java concurrent API which has provision for providing read / write locks which perform better in case there are lots of readers and a few writers. Pure synchronized keyword will block two readers also.
Comments
////////////FIRST METHOD USING SINGLE boolean////////////// public class ThreadTest implements Runnable { ThreadTest() { Log.i("Ayaz", "Constructor.."); } private boolean lockBoolean = false; public void run() { Log.i("Ayaz", "Thread started.." + Thread.currentThread().getName()); while (lockBoolean) { // infinite loop for other thread if one is accessing } lockBoolean = true; synchronizedMethod(); } /** * This method is synchronized without using synchronized keyword */ public void synchronizedMethod() { Log.e("Ayaz", "processing...." + Thread.currentThread().getName()); try { Thread.currentThread().sleep(3000); } catch (Exception e) { System.out.println("Exp"); } Log.e("Ayaz", "complete.." + Thread.currentThread().getName()); lockBoolean = false; } } //end of ThreadTest class //For testing use below line in main method or in Activity ThreadTest threadTest = new ThreadTest(); Thread threadA = new Thread(threadTest, "A thead"); Thread threadB = new Thread(threadTest, "B thead"); threadA.start(); threadB.start(); ///////////SECOND METHOD USING TWO boolean///////////////// public class ThreadTest implements Runnable { ThreadTest() { Log.i("Ayaz", "Constructor.."); } private boolean isAnyThreadInUse = false; private boolean lockBoolean = false; public void run() { Log.i("Ayaz", "Thread started.." + Thread.currentThread().getName()); while (!lockBoolean) if (!isAnyThreadInUse) { isAnyThreadInUse = true; synchronizedMethod(); lockBoolean = true; } } /** * This method is synchronized without using synchronized keyword */ public void synchronizedMethod() { Log.e("Ayaz", "processing...." + Thread.currentThread().getName()); try { Thread.currentThread().sleep(3000); } catch (Exception e) { System.out.println("Exp"); } Log.e("Ayaz", "complete.." + Thread.currentThread().getName()); isAnyThreadInUse = false; } } // end of ThreadTest class //For testing use below line in main method or in Activity ThreadTest threadTest = new ThreadTest(); Thread t1 = new Thread(threadTest, "a thead"); Thread t2 = new Thread(threadTest, "b thead"); t1.start(); t2.start(); Comments
To maintain predictability you must either ensure all access to mutable data is made sequentially or handle the issues caused by parallel access.
The most gross protection uses the synchronized keyword. Beyond that there are at least two layers of possibility, each with their benefits.
Locks/Semaphores
These can be very effective. For example, if you have a structure that is read by many threads but only updated by one you may find a ReadWriteLock useful.
Locks can be much more efficient if you choose your lock to match the algorithm.
Atomics
Use of AtomicReference for example can often provide completely lock free functionality. This can usually provide huge benefits.
The reasoning behind atomics is to allow them to fail but to tell you they failed in a way you can handle it.
For example, if you want to change a value you can read it and then write its new value so long as it is still the old value. This is called a "compare and set" or cas and can usually be implemented in hardware and so is extremely efficient. All you then need is something like:
long old = atomic.get(); while ( !atomic.cas(old, old+1) ) { // The value changed between my get and the cas. Get it again. old = atomic.get(); } Note, however, that predictability is not always the requirement.
Comments
Why do u need to do it?
Using only local variable/references will not solve most of the complex business needs. Also, if instance variable are immutable, their references can still be changed by other threads.
One option is use something like a SingleThreadModel, but it is highly discouraged and deprecated.
u can also look at concurrent api as suggested above by Kal