Google

May 29, 2013

Proxy design pattern for implementing thread safe wrappers


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;

/**
 * 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
        }   
    } 
}

Step 4: Here is the testing of proxied third-party library to prove that it is now 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();
        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();
        }
        
    }
}

The output of the above run is now thread-safe as shown below. Thanks to the proxy class that applies the lock.

Printing ..........1
Printing ..........2
Printing ..........3
Printing ..........4
Printing ..........5
Printing ..........6
Printing ..........7
Printing ..........8
Printing ..........9
Printing ..........10


Other design patterns - real life examples

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home