

|
|
Home >> Java
A scenario to work on multi-threading and a typical design :
This example is no way suggesting any best practice as such
in any context.
Someone asked me, whether there is a simple way of protecting
data from corruption when data is not used in thread safe
manner, like for example suppose there is a class A with a method
to print incremental values, but this class A should be extended
to a third party class SamplePlotter (this class is not coded as
thread safe per se), as this class SamplePlotter is having an
instance variable. This instance variable is suppose to hold the
count number/value and increment it gradually.
A.java
class A extends SamplePlotter
{
public void print(int a) {
System.out.println(""+setJ(a));
}
}
|
SamplePlotter.java
class SamplePlotter
{
int j;
public int setJ(int aj) {
j = j + aj;
return j;
}
}
|
client.java
public class client
{
public client() {
A a = new A();
Worker w = new Worker(a);
Thread th = new Thread(w, "TH1");
//Thread th1 = new Thread(w, "TH2");
th.start();
//th1.start();
}
public static void main(String[] args)
{
new client();
}
}
class Worker implements Runnable
{
private A a;
boolean st = false;
public Worker(A a) {
this.a = a;
}
public void run() {
for(int i=0;i<10;i++) {
if(Thread.currentThread().holdsLock(a)) {
a.notify();
}
System.out.println(Thread.currentThread().toString()+" ");
a.print(i);
try
{
if(Thread.currentThread().holdsLock(a)) {
Thread.currentThread().yield();
}
} catch (Exception ex){
ex.printStackTrace();
}
}
}
};
|
This class A < > SamplePlotter object hierarchy works
well in a single thread, and the output is okay/as expected,
shown as follows (by using the above client test program):
Thread[TH1,5,main]
0
Thread[TH1,5,main]
1
Thread[TH1,5,main]
3
Thread[TH1,5,main]
6
Thread[TH1,5,main]
10
Thread[TH1,5,main]
15
Thread[TH1,5,main]
21
Thread[TH1,5,main]
28
Thread[TH1,5,main]
36
Thread[TH1,5,main]
45
|
But if I am going to remove those commented lines in the public
client constructor, thus adding another thread TH2 in this program,
then the output is completely unacceptable/not expected, as
shown below:
Thread[TH1,5,main]
0
Thread[TH1,5,main]
1
Thread[TH1,5,main]
3
Thread[TH1,5,main]
6
Thread[TH2,5,main]
6
Thread[TH2,5,main]
7
Thread[TH2,5,main]
9
Thread[TH2,5,main]
12
Thread[TH1,5,main]
16
Thread[TH1,5,main]
21
Thread[TH1,5,main]
27
Thread[TH1,5,main]
34
Thread[TH2,5,main]
38
Thread[TH2,5,main]
43
Thread[TH2,5,main]
49
Thread[TH2,5,main]
56
Thread[TH1,5,main]
64
Thread[TH1,5,main]
73
Thread[TH2,5,main]
81
Thread[TH2,5,main]
90
|
As TH1 starts with 0 and TH2 starts with 6, and many more instances of
data corruption as far as this output is concern.
So what went wrong here?? any guesses, okay, let me continue
with my explanation here, as SamplePlotter class is having
an instance level variable "j", and A extends SamplePlotter
and the same instance of A is passed to Worker runnable instance
and to the Threads, then both the threads are going to use
this same instance of SamplePlotter, and eventually producing
an output, which is not as expected.
Now I think most of us might agree that in a multi-threaded
environment/context, who is going to write SamplePlotter
like this?? but assume that this SamplePlotter class is
part of a JAR file that is distributed from a separate
source and as developer you are not suppose to change the source
of this class, but use it by extending this SamplePlotter class
or any other wrapper.
Is there any ways we can solve this coding or thread-safe related
matter? So that both these Threads TH1 and TH2 will run, but separately
and not try to use same instance of SamplePlotter, and so it's instance
variable values.
Having written this problem statement, I spent sometime on this
and found one way to resolve this, this might be not an unique/only
way to solve this problem, and one can suggest any other way of
solving this and suggesting any design pattern as well.
I just tried to separate the instance of SamplePlotter and
provided both these Threads to work on separate instance/object
of SamplePlotter, thus solving this stepping on one another stuff.
So I created a wrapper and named it as SamplePlotterModified,
and class A now extends SamplePlotterModified, instead of
SamplePlotter, and the method signature remains the same, thus
no code change required for the client test program and SamplePlotter
class.
SamplePlotterModified.java
import java.util.Hashtable;
class SamplePlotterModified
{
private Hashtable ht;
public SamplePlotterModified() {
if(ht == null)
ht = new Hashtable();
}
public int setJ(int aj) {
if(ht.get(Thread.currentThread().getName()) == null) {
SamplePlotter sp = new SamplePlotter();
ht.put(Thread.currentThread().getName(),sp);
return sp.setJ(aj);
} else {
SamplePlotter sp = (SamplePlotter) ht.get(Thread.currentThread().getName());
return sp.setJ(aj);
}
}
}
|
this setJ method does some checking for the identifier, and here I have
used the Thread name as identifier for these Threads. This method setj
creates separate instance for each thread and passes on the result
back, and in order to avoid repetitively creating SamplePlotter
instance on separate setj method invocation, there is a Hashtable
instance is used to store/pool these instances for use.
client.java
public class client
{
public client() {
A a = new A();
Worker w = new Worker(a);
Thread th = new Thread(w, "TH1");
Thread th1 = new Thread(w, "TH2");
th.start();
th1.start();
}
public static void main(String[] args)
{
new client();
}
}
class Worker implements Runnable
{
private A a;
boolean st = false;
public Worker(A a) {
this.a = a;
}
public void run() {
for(int i=0;i<10;i++) {
if(Thread.currentThread().holdsLock(a)) {
a.notify();
}
System.out.println(Thread.currentThread().toString()+" ");
a.print(i);
try
{
if(Thread.currentThread().holdsLock(a)) {
Thread.currentThread().yield();
}
} catch (Exception ex){
ex.printStackTrace();
}
}
}
};
|
Now un commenting those lines for the second thread and executing this
test client program, following is the output as expected:
Thread[TH1,5,main]
0
Thread[TH1,5,main]
1
Thread[TH1,5,main]
Thread[TH2,5,main]
0
Thread[TH2,5,main]
1
Thread[TH2,5,main]
3
Thread[TH2,5,main]
3
Thread[TH1,5,main]
6
Thread[TH1,5,main]
10
Thread[TH1,5,main]
15
Thread[TH1,5,main]
6
Thread[TH2,5,main]
10
Thread[TH2,5,main]
15
Thread[TH2,5,main]
21
Thread[TH2,5,main]
28
21
Thread[TH1,5,main]
28
Thread[TH1,5,main]
36
Thread[TH1,5,main]
45
Thread[TH2,5,main]
36
Thread[TH2,5,main]
45
|
Now both TH1 and TH2, starts with 0 and ends with 45,
and this actually solves this problem statement.
I welcome all reader of this page to comment on this
approach and suggest any improvement/drawback/workaround
on these code and design aspect of this problem statement.
You can reach me at usingframeworks @ gmail . com.
Thanks for reading this !! Have an great coding time !!
Feedback as sent by Guddu on 20 Nov 2010 :
.............................................................................
Thanks for this approach discussed on this page.
In my thinking there will be need for managing the entries
in Hashtable, as I don't see any removal of the object after
us. One can think it as a drawback as one may have to manage
the Hashtable used as and when required or else size of this
Hashtable will increase and could be potential problem in
long run with an overhead of memory and performance.
Instead you can use ThreadLocal instance to store variable per
Thread and this way every Thread will have a separate instance
of the common business object. This way you might not have to
worry about managing a storage of instance and invalidating
it when required.
.............................................................................
If anything missed out , please let me know at
techienjoy at yahoo . com
References :
Tags: java comparator reflection
Tags: java example drag n drop
Tags: Java Interview Questions
Tags: java rmi tutorial stub skeleton
Tags: Java Thread Deadlock
Tags: Java Thread Design Scenarios
Tags: Java threadpoolexecutor
Tags: Java
DISCLAIMER :
The content provided in this page is not warranted and/or guaranteed by techienjoy.com.
techienjoy.com is not liable for any negative consequences that may result/arise from
implementing directly/indirectly any information covered in these pages/articles/tutorials.
All contents of this site is/are written and provided on an "AS IS" basis,
without WARRANTIES or conditions of any kind, either express or implied, including, without
limitation, merchantability, or fitness for a particular purpose. You are solely responsible
for determining the appropriateness of using or refering this and assume any risks associated
with this.
In spite of all precautions taken to avoid any typo in these pages, there might be some
issues like grammatical mistakes and typos being observed in these pages, techienjoy.com
extends sincerest apologies to all our visitors for the same.
|
| 

|