Approach No. 2 does not guarantee flow safety.
Two operations on sets are not atomic: first you delete the item, then add it to another collection. Some threads may, in the meantime, execute a read-only method to find out that the item is not in list 1 and has not yet been added to list 2. It depends on your application if this is valid.
On the other hand, it is also possible that: the read-only method iterates over list 1 first and finds that it contains the element x; at the same time, the update method executes and passes the x element; the read-only method continues and is repeated through list 2, in which it finds element x another element. Again, this depends on your application, whether it is permissible.
Other solutions are possible, but this will require more detailed information about what you are trying to achieve.
One obvious way would be to change the number 1 approach, and instead of using synchronization for each method, use read-write lock. You will block reading in each read-only mode and block writing in mutant mode.
You can also use two separate read-write locks. One for the first collection and one for the other. If your read-only methods are repeated on both lists, they will have to read - get both locks in front before doing anything. On the other hand, the mutation method would first have to write-get the first lock, and if it wants to pass the element, then it should write-get the second lock.
You will need to do some testing to see if it works well for you. However, there are even better ways to handle this, but you need to provide more details.
source share