0

As mentioned by Java_author,

When defining which variables form an object's state, we want to consider only the data that object owns....

In many case, ownership and encapsulation go together— the object encapsulates the state it owns and owns the state it encapsulates....

A class usually does not own the objects passed to its methods or constructors, unless the method is designed to explicitly transfer ownership of objects passed in (such as the synchronized collection wrapper factory methods)...


For ensuring thread safety, a non-thread-safe class need to draw a line on the state variables that it owns to ensure thread safety. Those state variables can be populated in class through generalization, association, Dependency Injection and what not.

My understanding is, Java author already gave(above) a parameter to assess state ownership for thread safety, the object encapsulates the state it owns and owns the state it encapsulates

Edit after this comment:

Below code taken from Listing 4.4/4.5 by Java_author, See the MonitorVehicleTracker class below owning locations that are populated thru copy constructor,

package responsive;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

class MutablePoint{
    public int x, y;
    
    public MutablePoint() {
        x=0; y=0;
    }
    
    public MutablePoint(MutablePoint p) {
        this.x = p.x;
        this.y = p.y;
    }
}

public class MonitorVehicleTracker {

    private final Map<String, MutablePoint> locations;
        
    public MonitorVehicleTracker( // Copy constructor
                        Map<String,MutablePoint> locations) {
        this.locations = deepCopy(locations);
        
    }
    
    public synchronized Map<String, MutablePoint> getLocations(){
        return deepCopy(locations);
    }
    
    public synchronized MutablePoint getLocation(String id) {
        
        MutablePoint loc = locations.get(id);
        return loc == null ? null : new MutablePoint(loc);
    }
    
    public synchronized void setLocation(String id, int x, int y) {
        MutablePoint loc = locations.get(id);
        if(loc == null) {
            throw new IllegalArgumentException("No such id: " + id);
        }
        loc.x = x;
        loc.y = y;
    }
    
    
    private static Map<String, MutablePoint> deepCopy(
            Map<String, MutablePoint> m){
        
        Map<String, MutablePoint> result = 
                    new HashMap<String, MutablePoint>();
        
        for(String id: m.keySet()) {
            result.put(id, new MutablePoint(m.get(id)));
        }
        
        return Collections.unmodifiableMap(result);
    }
    

}

that is used by GUI thread,

Map<String, Point> location = vehicles.getLocations();
for(String key: locations.keySet()){
   renderVehicle(key, locations.get)key));
}

and used by updater thread,

void vehicleMoved(VehicleMovedEvt evt){
   Point loc = evt.getNewLocation();
   vehicles.setLocations(evt.getVehicleId(), loc.x, loc.y);
}

For a non-thread-safe class to make thread-safe,

Do you agree with this parameter to apply synchronization policy on only those state variables that you own to ensure thread safety?

10
  • In #2, are you asking about, for example, container objects like arrays and vectors and trees? Or something else? Can you clarify? Commented Oct 10, 2017 at 1:30
  • @user1118321 For code class MyClass{ private HashSet<Person> mySet = new HashSet<Person>(); private int x;}, Does object's state ownership involve just x, and mySet or state ownership involves x, mySet and Person? Here, MyClass has composition relationship with HashSet<Person>. Similarly MyClass may have aggregation relationship with some other class Y, not just collections Commented Oct 10, 2017 at 2:59
  • @user1118321 second point from java author in the query is most of its answer around, which says, ownership and encapsulation go together-—the object encapsulates the state it owns and owns the state it encapsulates Commented Oct 10, 2017 at 3:06
  • 1
    "A class usually does not own the objects passed to its methods or constructors" -- incorrect. Some objects are designed to represent complex data structures, whereby the objects passed to the constructor are always owned. Immutable objects in particular often use this technique. You cannot generalize to say "usually" in either case; it depends on the types of objects. Commented Oct 10, 2017 at 16:14
  • You should specify the type of data being "owned" and exactly what "owned" means. Is it immutable data? There is no need to control that, as it cannot be modified. But you need a root object to access it. If it is mutable data, ownership can mean release of resources, sole access to modify, or both. Commented Oct 10, 2017 at 16:16

2 Answers 2

2

Don't over-conflate ownership with thread safety. To make a class threadsafe, you have to make it threatsafe. No more. No less. This means:

  • You cannot have two threads write to the same data at the same time
  • You cannot have one thread write to the data and one thread read from it at the same time.
  • The synchronization you add to the class does not violate any of the invariants of its methods (such as any guarantees of determinism).

Now it happens to be that encapsulation and ownership draw hard lines in the sand, and it can be easier to implement said threadsafety if you leverage these existing lines. As an example, one of the concerns in making multithreaded code is deadlocks, where two threads contend for two locks in a way that prevents either from making progress. If you put your synchronization at the edge of the encapsulated data, it becomes easy to prove that a particular class is deadlock free.

But ownership can't cover everything. Consider that ownership means you can do anything you want with the data. Read it, write it, store temporary copies of it. It's yours. In many multithreaded situations, you want many threads to be able to read the data simultaniously. In order to do this, the "owner" must give up the right to write to that data until the reads are complete. Generally speaking, our concept of "ownership" is not subtle enough to draw distinctions like this.

7
  • 1) For your point: ownership can't cover everything. I disagree, because in the above code, class MonitorVehicleTracker own the state locations and its internal members(key, value). As part of this ownership, MonitorVehicleTracker should ensure thread safety for those owned states. With this, I can say MonitorVehicleTracker is thread safe. 2) Btw, MonitorVehicleTracker does not own responsibility of adding new entry but, but MonitorVehicleTracker still owns state locations because for the reasons that locations need to be encapsulated by that class Commented Oct 10, 2017 at 19:11
  • @user1787812 Okay, I should be more precise. You can come up with examples where the code is so simple that ownership can cover everything. However, in the general case, it is more complicated. That being said, I see you are using the synchronized keyword, which is a very simple tool for multithreading which only handles the pure ownership cases as you describe. If that's the only tool you are using, then ownership will be a sufficient rule of thumb for ensuring safety (though you still need to make sure your multithreaded code doesn't violate any invariants)
    – Cort Ammon
    Commented Oct 10, 2017 at 19:11
  • When you say, multithreaded code doesn't violate any invariants. are you saying like? For a non-thread-safe Stack class, if stack is empty pop() simply returning null, make sense. For a thread-safe Stack class, if stack is empty then it make sense for pop() to wait() instead of returning null, because another thread may push() anytime. Commented Oct 10, 2017 at 19:20
  • Documenting Invariants for every thread-safe class, for the state variables that a thread-safe class own, will cover every scenario to be thread safe. This is what I understood from, when java author says, the object encapsulates the state it owns and owns the state it encapsulates. Encapsulation is nothing but protecting invariants. Do you still support that ownership can't cover everything to be thread safe? Commented Oct 10, 2017 at 19:37
  • An example for a thread-safe array may be that you can call size() to get the size of the array, and then call get(n) where n is an index smaller than size returned. That may be an explicit invariant of your vector, or an implicit one. When you add synchronization, you may find that the array size can change between calls to size and get, resulting in invalid code, even though you protected every state value that the object "owns" properly. What happened was your user's code depended on things to stay the same when they, in fact, changed on another thread.
    – Cort Ammon
    Commented Oct 10, 2017 at 20:44
0

Yes, objects formed via composition own the data objects they are composed of generally. That in itself does not ensure thread safety, though. There are a few reasons:

  1. The same object could be called on multiple threads, so it may actually make its own state inconsistent if you don't take proper precautions (such as using a mutex or similar construct)
  2. If Object A owns Object B, but Object C asks for a reference rather than a copy of Object B, then both A & C may be able to modify Object B if you aren't careful.

You also ask:

For code:

class MyClass { 
    private HashSet<Person> mySet = new HashSet<Person>(); 
    private int x;
}

Does object's state ownership involve just x, and mySet or state ownership involves x, mySet and Person? Here, MyClass has composition relationship with HashSet.

I would say that MyClass owns mySet and x and that mySet owns each Person it contains. (So transitively MyClass also owns each Person.) This is all assuming that nobody else has strong references to these objects. (And in general only one object should ever have a strong reference to a given object.)

4
  • 1) If you encapsulate a state variable then you own that state variable. A state variable can be mySet, or Person or x. For example, If you process/retrieve persons with encapsulation then you need to own Person state, before encapsulating that state variable. 2) Wrt your point on strong reference, state ownership of Person in MyClass does not mean, class Y does not have state ownership on Person Commented Oct 10, 2017 at 3:15
  • 1) Yes, because if you don't own it, some other object could destroy it while you're trying to use it. 2) In general, it doesn't. But you need to be careful if multiple objects have a strong reference to a single object so that the shared object doesn't end up leaking memory. Using a reference counting scheme can help with that. My Java is a bit rusty, so I'm a little hazy on how strong references work there, otherwise I'd be more specific. Commented Oct 10, 2017 at 3:19
  • person is member of hashset collection. The same person can be part of someother collection. I own state,, it does not mean I create and destroy it, because some object created some where is my private member, as mentioned in my second question Commented Oct 10, 2017 at 3:33
  • I'm not entirely sure I understand what you're asking here. It is possible for an object to have a weak reference to other objects. In that case you may not be creating and destroying them. I would still consider them part of your state, though, if your object is holding weak references to them. Commented Oct 10, 2017 at 5:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.