Stepping into the world of Threads

malindu ruwantha
6 min readJul 30, 2021

part 2 Synchronization

Synchronization

From the first part, we have discussed creating multiple threads. what will happen if the same thread having the access to the same instance of a class. Both of the threads are changing the states. This will bring some ambiguities to your application. As an example, let's take a Bank Account object.

class BankAccount {private Integer amount = 100;public Integer getBankAccountBalance() {return this.amount;}public void setBankAccountBalance(Integer amount) {this.amount = amount;}}

In the Runnable instance. create a new bank account object and implement the withdraw ().

public class BankAccountWithdraw implements Runnable {BankAccount bankAcc = new BankAccount();@Overridepublic void run() {withdraw(100, bankAcc);}public boolean isEligibleforWithdraw(Integer requestedAmount, BankAccount bankAcc) {if (bankAcc.getBankAccountBalance() >= requestedAmount) {return true;} else {return false;}}public void withdraw(Integer withdrawAmount, BankAccount bankAcc) {if (isEligibleforWithdraw(withdrawAmount, bankAcc)) {bankAcc.setBankAccountBalance(bankAcc.getBankAccountBalance() - withdrawAmount);System.out.println("success");} else {System.out.println("not enough balance");}}public static void main(String args[]) {Runnable r = new BankAccountWithdraw();Thread person1 = new Thread(r);Thread person2 = new Thread(r);person1.start();person2.start();}}

then create two thread instances with the same runnable instance

Whenever the application runs for person 1, the withdrawal will be successful and for person2 it not.

if we illustrate the flow it will be executed as follows.
for both instances we are creating a single bank account object with an amount of 100 .whenever person1 withdraw, withdraw() method is executed. It will validate the eligibility and the account would be deducted in the process.
in Line 23.

because of that whenever person 2 is validated, he would not be eligible because the account value is 0 and isEligibleForWithdrawal test will be failed.

but what will happen when the thread scheduler changes the executing thread from person1 to person 2 whenever person 1 is at Line 23. Now the person 1 has passed the isEligibleForWithdrawaltest but the account is not deducted yet.

Now, person2 is executing and he can also manage to go through isEligibleForWithdrawaltest because still the account is not deducted for person 1.

Now the person 2 will successfully withdraw the money and the account balance will be deducted to 0. As the person2 run() is over again, the person2 thread will start its execution from Line 23 and complete the transaction, bringing the bank account amount down to -100.

That's not correct !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Note: You can test this flow by appending a debug pointer at Line 23 and when person 1 thread comes, shifts back to person 2 thread and resume its execution. After finishing person2 shift back to person 1 and resume the execution.

You can overcome this faulty behavior by making withdraw () synchronized

The synchronized method will not let any other thread use the same method until the currently executing thread finishes its execution.

Synchronization and how locks work

If an object comes with a synchronized method that object will be having a lock. When a thread enters the synchronized method, that lock of the currently executing object instance will be acquired by the executing thread.

As there is only one lock per object, whenever one thread acquires it another cannot be entered into the synchronized method. when the execution of the synchronized method is finished lock will be released.

Following is some key facts about synchronized methods

  1. Only methods can be Synchronized (classes and variables cannot be synchronized)
  2. One lock for one object
  3. A class can have both synchronized and non-synchronized methods(Then the different threads can access the non-synchronized methods no locking will happen.)
  4. If the thread goes sleeps it holds the locks not releasing them
  5. Thread can acquire multiple locks
  6. If a thread has acquired the lock it can execute another synchronized method in the same object
  7. synchronization can be done to a block of code

8.When using synchronized blocks you need to give the object instance to be locked specifically

9.Synchronization can be done at the class level. Think of an instance synchronizing a static method. There is no difference in synchronizing a static method.

10.This can also be done using the static blocks

the class instance should be given

IMPORTANT

if you are having nonstatic synchronized methods in a class they will be blocked only for the same instance. If you invoke them from two different instances it wouldn't get blocked.

Common issues

1.Thread Deadlock
Imagine two threads are waiting for each other's locks. None of them can finish executions until the other releases the lock and no one can release the lock until it is executed.

2.Thread livelock

Imagine an occasion a single thread needs two locks for the execution

Thread A needs Lock Object P and Lock of Object Q
Thread B needs Lock Object P and Lock of Object Q

both Thread A and Thread B start executing parallelly.

what if Thread A gets lock P and trying to get lock Q, but it is already taken by Thread B.

Then Thread B whose with lock Q tries to get lock P but it is already taken by Thread A

Then for B to proceed Thread A will release the lock P and at the same time for A to proceed Thread B will release lock Q and Thread B is getting lock P. then again try to acquire lock Q. Again lock Q is being acquired by Thread A
SO now both of the Threads are not making any progress.

3.Thread Starvation

This issue happens when one thread holds the lock of a resource for a long time.
when other threads are trying to access the shared resource it's been held. Another reason for Thread starvation is the misdeclaration of thread priorities. If a long-running and highly prioritized thread came in holding a shared resource. It might be again and again selected from the thread pool until the execution is finished. So if the mentioned Thread gets hold of a synchronized shared resource it would be blocked.

4.Wait() , Notify(), NotifyAll()

Above are the methods from the object class that helps in thread communication. Three of these methods should be called within a synchronized block or a method.

a.wait() will be stopping its execution and waits until notify() method is called from the instance of a

If multiple threads are waiting for a particular Thread notifyall()
method will bring all the waiting threads back to the runnable pool.

So we have covered the basics of Java Threads. Let's explore more about the concurrency in next story

--

--