To analyze, first you list all possible synchronization orders. they must be consistent with programming order. In your example, there are 6 possible orders.
1 2 3 4 5 6
w(a) w(a) w(b) w(a) w(b) w(b)
r(b) w(b) w(a) w(b) w(a) r(a)
w(b) r(b) r(b) r(a) r(a) w(a)
r(a) r(a) r(a) r(b) r(b) r(b)
Each order establishes some happens-before relations. In (1), we have w(a) happens-before r(a). In (6), we have w(b) happens-before r(b). In (2)-(5), we have both.
For every possible order, given the happens-before relations established by it, you need to analyze the execution to make sure it does what you want.
If that sounds too hard, it is. In real life, we usually limit ourselves with simpler situations, where only one object is locked/released, or only one volatile variable is read and write. Then it's not too complex.