Большое спасибо всем за ответы. Проблема оказывается в том что инкрементирование (counter += add;) это не атомарная операция . Я этого не знал. Отсюда и каждый раз разные и неправильные значения на выходе. Происходит примерно так:
Т1: Поток 1 считывает i из памяти, она равна 1.
Т2: Поток 1 инкрементит до 2 у себя в регистре.
Т3: Происходит context switch, поток 1 саспендится, содержимое его регистров сбрасывается во временное хранилище, а в процессор записывается содержимое регистров для потока 2, и этот поток начинает работать.
Т4: Поток 2 считывает i из памяти, она равна 1.
Т5: Поток 2 инкрементит до 2 у себя в регистре.
Т6: Поток записывает в память 2.
Т7: Опять context switch, начинает работать первый поток.
T8: Поток 1 записывает в память 2.