LOCK is a prefix byte (F0h). While the instruction after it runs, the 8086 activates its LOCK output pin, telling other bus masters (a second processor, the 8087, a DMA controller) not to take the bus. An instruction that reads memory and then writes it back, such as XCHG or INC on a memory operand, then cannot have another processor slip in between the read and the write.

A semaphore with XCHG

The classic use is a flag shared between processors. LOCK XCHG swaps a register with memory as one step: it stores 1 and returns what was there. If the old value was 0 the flag was free and you now own it; if it was 1 someone else has it and you try again. A plain MOV back to 0 releases it.

MOV AL, 1
LOCK XCHG AL, FLAG1 ; take it; AL = the old value
MOV BL, AL ; 0: it was free, we own it
MOV AL, 1
LOCK XCHG AL, FLAG1 ; a second try
MOV BH, AL ; 1: already taken
MOV AL, BL
ADD AL, '0' ; digit -> character
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
MOV AL, BH
ADD AL, '0' ; digit -> character
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
MOV FLAG1, 0 ; release
FLAG1 DB 0

In a PC with a single 8086 or 8088, LOCK changes almost nothing that a program can see: interrupts only come between instructions anyway, so an instruction is already atomic as far as interrupt handlers are concerned. It matters for multiprocessor boards and for memory shared with other bus masters.

Pitfalls

LOCK REP with a string instruction is two prefixes, and an interrupt in the middle makes the 8086 resume with only the last one (see REP). On the 8086 the prefix can be put on any instruction.

Later CPUs: the 80386 and later accept LOCK only on read-modify-write instructions with a memory operand and raise an invalid-opcode exception otherwise, and an XCHG with memory locks the bus by itself, with or without the prefix.