Q. What are the different ways you can make an object thread-safe?
A.
- Synchronize critical sections: An object's critical sections are those methods or blocks of code within methods that must be executed by only one thread at a time. By using Java's synchronized keyword, you can guarantee that only one thread at a time will ever execute the object's critical sections.
- Make the object immutable. Immutable objects are, by their very nature, thread-safe simply because threads have to be able to write to an object's instance variables to experience a read/write or write/write conflict.
- Use a thread-safe wrapper: by applying the proxy design pattern. let's have a look at an example.
Step 1: Here are the sample third party interface and implementation classes.
Here is the Interface
package com.arul;
public interface ThirdPartyInterface
{
abstract void someMethod();
}
Here is the implementation
package com.arul;
public class ThirdPartyImpl implements ThirdPartyInterface
{
private int someVar = 0;
@Override
public void someMethod()
{
//some not thread safe functionality
System.out.println("Printing .........." + ++someVar);
}
}
Step 2: Here is the testing of original third-party library to prove that it is not thread-safe.
package com.arul;
public class TestThreadSafeThirdParty implements Runnable
{
private ThirdPartyInterface tstp = null;
public TestThreadSafeThirdParty(ThirdPartyInterface tstp)
{
this.tstp = tstp;
}
public static void main(String[] args)
{
ThirdPartyImpl localTp = new ThirdPartyImpl();
TestThreadSafeThirdParty test = new TestThreadSafeThirdParty(localTp);
//create 2 threads
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
thread1.start();
thread2.start();
}
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
tstp.someMethod();
}
}
}
The output of the above run is not thread-safe as shown below
Printing ..........1
Printing ..........3
Printing ..........2
Printing ..........4
Printing ..........5
Printing ..........6
Printing ..........7
Printing ..........9
Printing ..........8
Printing ..........10
Step 3: Here is the thread safe implementation that acts as a proxy to the subject, and its responsibility is to add thread-safety.
package com.arul;Step 4: Here is the testing of proxied third-party library to prove that it is now thread-safe.
/**
* proxy class that applies thread safety to
*/
public class ThreadSafeThirdPartyImpl implements ThirdPartyInterface
{
private final Object lock = new Object();
private final ThirdPartyInterface subject;
public ThreadSafeThirdPartyImpl(ThirdPartyInterface subject)
{
this.subject = subject;
}
@Override
public void someMethod()
{
//lock to provide thread safety via this proxy class
synchronized (lock)
{
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
subject.someMethod(); //access the unsafe method
}
}
}
package com.arul;The output of the above run is now thread-safe as shown below. Thanks to the proxy class that applies the lock.
public class TestThreadSafeThirdParty implements Runnable
{
private ThirdPartyInterface tstp = null;
public TestThreadSafeThirdParty(ThirdPartyInterface tstp)
{
this.tstp = tstp;
}
public static void main(String[] args)
{
ThirdPartyImpl localTp = new ThirdPartyImpl();
ThreadSafeThirdPartyImpl localTsTp = new ThreadSafeThirdPartyImpl(localTp);
TestThreadSafeThirdParty test = new TestThreadSafeThirdParty(localTsTp);
//create 2 threads
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
thread1.start();
thread2.start();
}
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
tstp.someMethod();
}
}
}
Printing ..........1
Printing ..........2
Printing ..........3
Printing ..........4
Printing ..........5
Printing ..........6
Printing ..........7
Printing ..........8
Printing ..........9
Printing ..........10