Glad you asked!

synchronized locks code, atomically locks data.

It maps to my thinking better, because what I'm interested in is manipulating two or more resources at the same time. I'm not interested in making sure two threads aren't in the same region of code at the same time.

> What error does it protect you against compared to synchronising on list?
Synchronized is perfectly fine for my example. I should have picked a better example of two resources (instead of list and size of list), because synchronized gets trickier once you start combining different pieces.

For example, I have two bounded lists, and I want to transfer an element from one to the other. I've already written synchronized versions of remove and insert, so let's try with them:

transfer(otherlist) {
x = otherlist.synchronizedRemove();
this.synchronizedInsert(x);
}

There will be a moment in time where the outside world can't see x in either list. Maybe I crash and x is gone for good. Or maybe the destination list becomes full and I'm left holding the x with nowhere to put it. So what is to be done? I could synchronize transfer but that still wouldn't fix the vanishing x, or the destination filling up. So I paid the performance cost of taking two/three locks and I've still ended up buggy.

I think the fix here is to lock each list, then no-one else can access them and it should fix the vanishing x:

transfer(otherlist) {
synchronized(this) {
synchronized(otherlist) {
x = otherlist.synchronizedRemove();
this.synchronizedInsert(x);
}
}
}

I think that's correct? But now I have taken too many locks - I only needed remove and insert not synchronizedRemove and synchronizedInsert. And now I've introduced deadlock possibility - if two transfers are attempted in opposite directions.

I can fix the too many locks problem, by exposing non-synchronized remove and insert and have transfer call them instead. But then callers and I will accidentally call the wrong one. I break any pretence of encapsulation by exposing unsafe and safe versions of a method. The deadlock is harder to fix. I'd need to synchronize the lists in some agreed order (and have everyone else obey that ordering in their other methods too!).

Instead, I want my implementation to look something like:

BoundedList {

transactional Int sz;
transactional List list;

transactional insert(x) {
if (sz < 4) {
list.insert(x);
sz++;
}
}

transactional remove() {...}

transactional transfer(otherlist) {
x = otherlist.remove();
this.insert(x);
}
}

> What happens if .insert() also uses atomically{} somehow?

A good implementation would throw a compile-time error. A bad implementation could throw a runtime error.

In order to do this, transactional actions would need to be marked as such - to prevent mixing them up. atomically by definition is a non-transactional action (because it's the thing that commits all the steps to the outside world) so if you find an atomically inside a transaction, it's a type error.

You've already used a system like this if you've worked with any decent SQL implementation:

BEGIN TRANSACTION

UPDATE accounts
SET money = money - 50
WHERE accountId = 'src'

UPDATE accounts
SET money = money + 50
WHERE accountId = 'dest'

COMMIT TRANSACTION

I didn't take any 'locks'. I just wrapped two perfectly good individual actions and said 'run them both or not at all'. Though to be fair I am getting a lot of grief in another thread by suggesting that even novices could wrap up their SQL like that without getting it wrong.