<< Previous Chapter | Next Chapter >> |
Synchronization (Multithreading)
in Java:
When all threads are
sharing some common resource, then there might be possibility of data loss,
overwrite etc. when one object is shared between multiple threads, in such case
there should be a process which ensures that when one thread is accessing a
shared resource then another thread should not be allowed to access it. This process
is known as synchronization.
This situation occurs in
web based and distributed applications.
package
dev21century.threading;
public class Thread1 {
int x, y, result;
int add(int a, int b)
{
x = a;
y = b;
result = x + y;
return result;
}
}
Thread1 calls above
method with 10, 20
Thread2 calls above
method with 30, 40
Thread3 calls above
method with 50, 60
There are 2 ways of implementing
synchronization – one is to synchronize whole method and another is to
synchronize a particular block.
Example
synchronized
int
add(int a, int b) { }
Or
Synchronized
{
//block
code
}
Rule
– if a thread is entering
in a synchronized method or a synchronized block for a particular object then
that thread must have the monitor (lock) of that object. So, when thread1 starts,
it takes the lock first and then run, and if by chance while executing it went
to the blocked state then it will take the lock with it. Now, when another
thread enters with same object then obviously it cannot acquire the lock of
that object and will not able to run and will wait until the first thread is
completed to take the lock of the object.
So, this way we can stop
data corruption.
Example:
Create a file RunSync.java in
dev21century.threading package:
package
dev21century.threading;
class Shared
{
int x;
synchronized void
show(String s, int a)
{
x = a;
System.out.println("Entered
in method " + s + " " + x);
try{
Thread.sleep(2000);
}
catch(Exception
e)
{
System.out.println(e);
}
System.out.println("Exit
from method " + s + " " + x);
}
}
class
CustomThread1 extends Thread
{
Shared s;
CustomThread1(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
10);
}
}
class
CustomThread2 extends Thread
{
Shared s;
CustomThread2(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
20);
}
}
class
CustomThread3 extends Thread
{
Shared s;
CustomThread3(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
30);
}
}
public class
RunSync {
public static void
main(String[] args) {
Shared st = new
Shared();
new
CustomThread1(st, "one");
new
CustomThread2(st, "two");
new
CustomThread3(st, "three");
}
}
In above example you will
notice that each "Entered in..." message will be printed in a gap of 2 seconds. This is because
when one thread enters the show method it will first take the lock of shared
object, will print message and sleep for 2 seconds, and as the show method is
synchronized, that means no other thread can enter this method with same object
for at least 2 seconds that’s why it takes 2 seconds for next message.
Now, just remove
synchronized keyword and try once again, this time all 3 messages will be printed
without waiting. So this is the beauty of synchronization.
Quick
Question – if we have
2 methods synchronized then will the second thread call the second thread?
Answer
– it will never allow
calling second method as well because lock is at object level, and same object
is getting passed for both methods (if we will remove “synchronized” then second
method will be invoked.
Rule
– the implicit lock (monitor)
is also maintained at class level. In case of non static methods, the object lock
is used, but in case of static methods the class level lock is used.
If one method is called
through class and another through Object, then the lock will always be checked
for class if using static method.
If one method is
static and another is non static then both will run separately with class and
object level locks.
Synchronized block:
In java we can make a
block as synchronized instead of synchronizing complete method. This is
beneficial because synchronizing a method may degrade the performance because,
all statements that are not participating in multithreading will also be
locked.
Syntax:
public void show()
{
synchronized (this)
{
//code which needs synchronization
}
//code which does not
need synchronization
}
In case of
synchronized method, the lock is always acquired on current object, but in case
of synchronized block the lock can be acquired on any object.
synchronized block is also
used to make the object of any class synchronized, by default objects are not
synchronized, but we can synchronize it by using synchronized block.
lets modify our
existing program as follows:
package dev21century.threading;
class Shared
{
int x;
void show(String s, int a)
{
System.out.println("Entered in method " + s);
synchronized(this)
{
x = a;
System.out.println("Entered in synchronized
block " + s + " " + x);
try {
Thread.sleep(2000);
}
catch (InterruptedException e) {
System.out.println(e);
}
System.out.println("Exit from synchronized
block " + s + " " + x);
}
}
}
class CustomThread1 extends Thread
{
Shared s;
CustomThread1(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(), 10);
}
}
class CustomThread2 extends Thread
{
Shared s;
CustomThread2(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(), 20);
}
}
class CustomThread3 extends Thread
{
Shared s;
CustomThread3(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(), 30);
}
}
public class RunSync {
public static void main(String[] args) {
Shared st = new Shared();
new CustomThread1(st, "one");
new CustomThread2(st, "two");
new CustomThread3(st, "three");
}
} |
Output:
Now, in this example you will notice that all “Entered in method”
messages will be printed without any wait, but “Entered in synchronized block…”
messages will be printed with 2 seconds gap because they are in synchronized
block, and without printing “Exit..” message the next “Entered in sync block..”
message is not getting displayed. It means no threads are allowed in
synchronized block unless the current thread is completed properly.
Now, lets understand how we can achieve this noncurrent:
In above example we
have synchronized the current object, but many times in development we face the
situation where we need to synchronize other objects as well. So lets modify
our program as follows, here we have created a new class called Temp and tried
to synchronize it from outside:
package
dev21century.threading;
class Temp
{
void
display(String s)
{
System.out.println("Entered
in display Method.." + s);
try {
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Exit
from display method.." + s);
}
}
class Shared
{
int x;
Temp temp = new Temp();
void show(String s, int a)
{
System.out.println("Entered
in show method " + s);
synchronized(temp)
{
temp.display(s);
try {
Thread.sleep(2000);
} catch
(InterruptedException e) {
System.out.println("Exit
from synchronized block " + s + " " + x);
}
}
}
}
class
CustomThread1 extends Thread
{
Shared s;
CustomThread1(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
10);
}
}
class
CustomThread2 extends Thread
{
Shared s;
CustomThread2(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
20);
}
}
class
CustomThread3 extends Thread
{
Shared s;
CustomThread3(Shared s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.show(Thread.currentThread().getName(),
30);
}
}
public class
RunSync {
public static void
main(String[] args) {
Shared st = new
Shared();
new
CustomThread1(st, "one");
new
CustomThread2(st, "two");
new
CustomThread3(st, "three");
}
}
|
Quick Question – why suspend() and resume()
are deprecated?
Answer – the suspend() method
puts the thread in blocked pool infinitely and resume() brings it back. The problem
is when a thread is suspended by suspend() method, it will take the lock with
it, and in case if no one calls the resume() method then another thread can
never enter that block. It will be a dead lock situation.
The alternate of
suspend() and resume() is wait() and
notify() both these methods belongs
to Object class. The wait() method will acquire the lock and put thread in blocked
pool.
notify() method will
pull the thread from blocked pool which was blocked by wait() method.
Lets take this
example:
package
dev21century.threading;
class Shared
{
int flag = 0;
int data = 0;
synchronized public void
submit()
{
flag = 1;
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
data = 10;
System.out.println("value
submitted");
notify();
}
synchronized int
withdraw()
{
if(flag == 0)
{
try {
System.out.println("wait
block");
wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return (data);
}
}
class
Thread1 extends Thread
{
Shared
s;
Thread1(Shared
s, String str)
{
super(str);
this.s=s;
start();
}
public void run()
{
System.out.println("data
= " + s.withdraw());
}
}
class
Thread2 extends Thread
{
Shared
s;
Thread2(Shared
s, String str)
{
super(str);
this.s=s;
start();
}
public void run()
{
//System.out.println("data
= " + s.withdraw());
s.submit();
}
}
public class
RunSync {
public static void
main(String[] args) {
Shared sharedObj = new
Shared();
new
Thread1(sharedObj, "one");
new
Thread2(sharedObj, "two");
}
}
Output:
There is another deadlock condition which might occur,
so we should always avoid such kinds of programming, lets see this example:
package
dev21century.threading;
class Temp
{
void
display(String s)
{
System.out.println("Entered
in display Method.." + s);
System.out.println("Exit
from display method.." + s);
}
}
class
CustomThread1 extends Thread
{
Temp t1, t2;
CustomThread1(Temp t1, Temp t2, String
str)
{
super(str);
this.t1 = t1;
this.t2 = t2;
start();
}
public void run()
{
synchronized(t1)
{
try {
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
t1.display(Thread.currentThread().getName());
synchronized(t2)
{
t2.display(Thread.currentThread().getName());
}
}
}
}
class
CustomThread2 extends Thread
{
Temp t1, t2;
CustomThread2(Temp t1,Temp t2, String
str)
{
super(str);
this.t1 = t1;
this.t2 = t2;
start();
}
public void run()
{
synchronized(t2)
{
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
t2.display(Thread.currentThread().getName());
synchronized(t1)
{
t1.display(Thread.currentThread().getName());
}
}
}
}
public class
RunSync {
public static void
main(String[] args) {
Temp temp1 = new
Temp();
Temp temp2 = new
Temp();
new
CustomThread1(temp1, temp2, "one");
new
CustomThread2(temp1, temp2, "two");
}
}
You will have to
explicitly stop the program. This is because in this program the thread 1 has
synchronized object 1 and thread 2 has synchronized the object 2. Hence both
are waiting for each other. Always avoid such type of programming.
interrupt() method:
this is a Thread class
method which is used to request to the JVM to terminate a thread. As we cannot
force JVM for garbage collection, we can only request by using System.gc()
method, similarly we cannot force JVM to terminate a thread. We can only
request. In each class there is boolean flag which is set to true if interrupt
request has been raised. In 99% cases JVM does not terminate the thread,
<< Previous Chapter | Next Chapter >> |
No comments:
Post a Comment