I am writing a static utility method. I know methods isEmpty() and isNew() are threadsafe. In the getTotal(...) method I am using StringBuilder as parameter along with String and int. StringBuilder is mutable. Is getTotal() threadsafe? If so explain why it is even though StringBuilder is mutable. I am not sure if getCharge() is thread safe because it's invoking the method getTotal(). Can someone tell if it is threadsafe or not?
public class NewStringUtils {
public static boolean isEmpty(String s){
return (s == null || s.trim().length()==0);
}
public static boolean isNew(String code){
return( "1009".equals(code) || "1008".equals(code) );
}
//StringBuilder is mutable -- is the below method threadsafe
public static int getTotal(StringBuilder sb,String oldCode ,int a, int b){
//Is it Threadsafe or not .If so just bcz every thread invoking this method will have its own copy and other threads can't see its value ??
int k =0;
if("1011".equals(oldCode) && "1021".equals(sb.toString()) {
k = a+b;
}
return k;
}
// is the below method threadsafe
public static int getCharge(String code,String oldCode,int charge1,int charge2){
int total =0;
StringBuilder sb = new StringBuilder("1021");
if(!NewStringUtils.isEmpty(code)){
if(NewStringUtils.isNew(code)){
//here invoking a static method which has StringBuilder(Mutable) as a parameter
total = NewStringUtils.getTotal(sb,oldCode,charge1,charge2);
}
}
return total;
}
}
getTotal is not thread-safe because it is possible that two or more than two threads are passing same reference of StringBuilder to the getTotal method as arguement and modifying the StringBuilder before passing it ...
And your getCharge is completely Thread-safe because here each thread is making local copy of StringBuilder Object in there own stack. So not to worry about thread safety of getCharge.
Here is the short Demo which is demonstrating why getTotal is not ThreadSafe:
class ThreadSafe
{
public static int getTotal(StringBuilder sb,String oldCode ,int a, int b)
{
int k =0;
try
{
System.out.println(Thread.currentThread().getName() + " have sb as " + sb);
Thread.sleep(100);//Added intentionally to show why it is not thread safe.
}
catch (Exception ex)
{
System.out.println(ex);
}
if("1011".equals(oldCode) && "1021".equals(sb.toString()))
{
System.out.println(Thread.currentThread().getName()+" is within if loop");//Thread1 should be within this if block but it's not.
k = a+b;
}
return k;
}
public static void main(String[] args)
{
final StringBuilder sBuilder = new StringBuilder();
Thread th1 = new Thread(new Runnable()
{
public void run()
{
sBuilder.append("1021");
getTotal(sBuilder,"1011",10,20);
}
},"Thread1");
Thread th2 = new Thread(new Runnable()
{
public void run()
{
sBuilder.append("22");
getTotal(sBuilder,"1011",10,20);
}
},"Thread2");
th1.start();
th2.start();
}
}
At my system , the output is:
Thread1 have sb as 1021
Thread2 have sb as 102122
Assuming that Thread1 started before Thread2 which indeed happened here(as seen in output), If getTotal would have been thread-safe then , the statement Thread1 is within if loop must have been printed in output. But it is not . WHY? Because after Thread1(with sb = "1021") went to sleep in getTotal method the thread is preempted by Thread2. Thread2 appended 22 to the existing StringBuilder object sBuilder(i.e the new value now is 102122). Again when sleep method is called for Thread2, it is preempted by Thread1. Thread1 goes to if construct , but till now the content of sBuilder is changed to 102122. So if condition becomes false for Thread1. And it does'nt print that line Thread1 is within if loop which we were expecting if we consider getTotal to be thread-safe. Hence it proves that getTotal is not Thread-safe.
How can we make getTotal ThreadSafe?
- By passing
Stringinstead ofStringBuilder.
getTotal() is thread-safe in terms of being re-entrant (i.e., it can be called by multiple threads simultaneously). In terms of doing its job correctly, however, getTotal() is not thread-safe if another thread modifies the "sb" argument. This conditional safety should be explicitly stated in the documentation for that method.
If getTotal() were private rather than public and only called by getCharge(), then it would be unconditionally thread-safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With