The call receiver.lock() takes &Mutex<...>, so it would normally only require a borrow. Because of this, it is not normally moved into the closure. id is in a similar situation: println! only borrows arguments you pass into it, so id is only ever borrowed too, so it would also normally remain owned by the external environment.
But in order to spawn it on a thread, the closure must own all the things it uses, and can't borrow any from the external environment. So the move keyword forces the closure to move/take ownership of everything it uses. Specifically, receive and id.