У вас есть обычная память, ссылка «currentpos» и объект Point и его поля за ней, разделенные между двумя потоками без синхронизации. Таким образом, не существует определенного порядка между сообщениями, которые происходят с этой памятью в основном потоке, и чтениями в созданном потоке (назовите его T).
Основной поток делает следующие операции записи (игнорирующие начальной настройки точки, приведет к рх, ру, имеющие значения по умолчанию):
Поскольку нет ничего особенного в этих сообщениях с точки зрения синхронизации/барьеров, среда выполнения позволяет разрешить поток T видеть их в любом порядке (основной поток, конечно, alw AYS видит запись и чтение упорядочивается по заказу программы), и происходит в любой точке между чтениями в Т.
Так T делают:
- читает currentpos к р
- чтения точек и р (в любом порядке)
- сравнить, и возьмите ветку
- чтения точек и ру (или порядок) и вызовите System.out.println
Учитывая, что между основными принципами записи и чтением в T нет упорядочивающих отношений, существует несколько способов, которые могут привести к вашему результату, поскольку T может видеть запись основного файла в currentpos до записей в currentpos.y или currentpos.x :
- Он сначала считывает currentpos.x, прежде чем произойдет запись x - получает 0, затем читает currentpos.y до того, как возникла запись y - получает 0. Сравните evals с true. Запись становится видимой для T. System.out.println.
- Сначала он считывает currentpos.x, после того, как запись x произошла, затем читает currentpos.y до того, как произошла запись y - получает 0. Сравните evals с true. Записи становятся видимыми для T ... и т. Д.
- Он читает currentpos.y во-первых, до того, как произошла запись y (0), затем читает currentpos.x после записи x, evals в true. и т.д.
и так далее ...Здесь есть несколько гонок данных.
Я подозреваю, что недостатки предположение здесь думает, что пишет, что результатом этой линии становятся видимыми для всех потоков в программном порядке нити его выполнения:
currentPos = new Point(currentPos.x+1, currentPos.y+1);
Java не дает такой гарантии (это было бы ужасно для производительности). Что-то еще нужно добавить, если ваша программа нуждается в гарантированном порядке записи относительно чтения в других потоках. Другие предложили сделать окончание x, y окончательным или, альтернативно, сделать текущую ситуацию неустойчивой.
- Если вы сделаете окончательные поля x, y, то Java гарантирует, что записи их значений будут видны до появления конструктора во всех потоках. Таким образом, поскольку назначение currentpos после конструктора, T-поток гарантированно увидит записи в правильном порядке.
- Если вы делаете currentpos volatile, тогда Java гарантирует, что это точка синхронизации, которая будет тотально упорядочена по другим точкам синхронизации. Как и в основном, записи в x и y должны происходить до записи в currentpos, тогда любое чтение currentpos в другом потоке должно также видеть записи x, y, которые были ранее.
Использование final имеет то преимущество, что оно делает неизменным поля и, таким образом, позволяет кешировать значения. Использование volatile приводит к синхронизации при каждой записи и чтении currentpos, что может повредить производительность.
Смотри главу 17 из языка Java Spec для окровавленных деталей:. http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html
(Начальный ответ предполагается более слабую модель памяти, так как я не был уверен, что JLS гарантировано летучий было достаточным ответом отредактированных, чтобы отразить комментарий от assylias , указывая на то, что модель Java сильнее - бывает - раньше транзитивна - и поэтому достаточно волатильность на currentpos).
12 milion оборудования? мне действительно интересно, как это могло произойти ... почему вы используете пустой блок синхронизации: synchronized (this) {}? –
Это даже не удаленная по потоку. –
@MattBall, и это уменьшенный код. Очевидно, что запись в 'currentPos' не« слушается »перед чтением, но я не вижу, как это может быть проблемой. – Dog