Very good question! The whole point of synchronizing this at all is so that while the RDD and its splits are being serialized for being sent as a Task, the switch in the dependencies should not take place. Doing a finer granularity locking by locking individual RDDs/RDDCheckpoints requires all the RDDs involved in a task to be locked. This would be slightly tricky to implement, as one must traverse through the RDD dependency graph to figure out which RDDs will be serialized along with the task and lock only those RDDs. Locking multiple objects always leads to a possibility of deadlocks, etc (though my gut feeling says it would be fine as we locking along a ordered DAG of objects). To play it safe, I chose to keep it simple by having a global lock. Furthermore, we dont expect the checkpoint operation to be performed very frequently (maybe once a second at max), so most of the time, only the task launching thread is going to take the lock. So lock contention is not much of a performance issue here. Hence I felt that keeping it simple was fine for now. In future we may have rethink this.