Volatile and Synchronized to Solve Race Condition: Singleton Member Field

Viewed 812

I was having some problem trying to understand and fix errors reported from Fortify scan. I have this class:

public class DaoImpl extends BaseDaoImpl {
    private static volatile String sNric;
    
    synchronized private void setInfo(InfoTO pers) {
        sNric = pers.getNRIC();
    }

    synchronized public InfoTO getInfo() { 
        InfoTO pers = new InfoTO();
        sNric = retrieveDetail();
        pers.setNRIC(sNric);
    }

    synchronized private String retrieveDetail() {
        // some logic to get info from database
    }
}

My code was originally without the static volatile and synchronized keyword. And Fortify was reporting Race Condition: Singleton Member Field warning at the variable declaration of sNric as well as sNric = retrieveDetail();

I went to research and found this solution. However, I am not very sure on the concept of volatile with synchronized. Will the proposed solution above causing some deadlock issue?

2 Answers

The "concept" of volatile with synchronized is that you probably shouldn't do it.

If you use synchronized in all of the methods that access and update a shared variable (such as your sNric variable) then declaring the volatile is redundant and inefficient.

As to your question about deadlocks, I cannot see any way that you could get deadlocks based solely on the code above. However, you haven't shown us the code for the InfoTO or the code that uses these classes. It is not impossible for a deadlock to occur involving the DaoImpl instance lock and other locks.

If you are concerned that getInfo calling this.retrieveDetail might deadlock. There is only one (DaoImpl instance) lock involved here, and Java primitive locks are reentrant. (A thread will not be blocked if it tries to acquire a primitive lock that it already holds.)

Finally, you should if you are concerned about thread-safety, check that setNRIC and getNRIC are thread-safe. If they are not, I don't think that the above is handling the InfoTo objects safely.


Note that you cannot reason about the thread-safety of a class unless you take account of the other classes that it depends on AND the way that it used / intended to be used.

You have not really given enough information and code details for a certain Answer. But I will give it a shot.

AtomicReference

To quote the comment above by scottb:

Thread safety of shared variables is about visibility and atomicity of state transitions.

I often prefer the use of the Atomic… classes to address both visibility and atomicity. These classes can provide an alternative to volatile and synchronized.

In this case, we can use AtomicReference class to hold as its payload a reference to your current desired String value. Notice that we mark it final as the reference to the AtomicReference object itself will never change. Its payload, a reference (pointer) taking us to the desired String object, does change. At one moment it may point to the String value "dog" while a moment later it may point to the String value "cat". But the container of either String is always the very same AtomicReference object, a wrapper around that contained text, that contained String object.

If your InfoTO class looked like this:

package work.basil.example;

public class InfoTO
{
    private String nric ;

    public String getNric ( ) { return this.nric; }

    public void setNric ( String nric ) { this.nric = nric; }
}

…then your DaoImpl might look something like this:

package work.basil.example;

import java.util.concurrent.atomic.AtomicReference;

public class DaoImpl
{
    private AtomicReference < String > sNric;

    private void setInfo ( InfoTO pers )
    {
        this.sNric.set( pers.getNric() );
    }

    public InfoTO getInfo ( )
    {
        InfoTO pers = new InfoTO();
        String s = retrieveDetail();
        pers.setNric( s );
        return pers ;
    }

    private String retrieveDetail ( )
    {
        return this.sNric.get();
    }
}

Your lines:

        sNric = retrieveDetail();
        pers.setNRIC(sNric);

…do not make sense to me. You use a field to hold what is a temporary value. So I substituted a local variable instead.

Your retrieveDetail method makes no sense to me. You seem to be returning from calls to a database the very same string value you are caching in the field sNric. So I changed that method to access the cached field sNric. This seems more consistent with your intended logic, and more importantly, shows both the getter and setter of the AtomicReference in action.

Of course, as others said, you may well have other thread-safety issues in the substantial code you did not show us.

Related