Статические переменные и многопоточность в Java
Статический член класса присутствует только как один экземпляр для процесса или потока? Что означает, что каждый поток имеет свою собственную копию статической переменной-члена класса?
Мое предположение за процесс, я прав?
7 ответов
Статические поля дают одно значение для каждого загрузчика классов.
Если вы хотите значение для каждого потока, сделайте статический ThreadLocal<T>
,
static
поля имеют одно значение для каждого загрузчика классов, но я думаю, что суть вашего вопроса заключается в следующем:
каждый поток имеет свою собственную копию статической переменной-члена класса
Это правильно, хотя дьявол кроется в деталях. Каждый поток может иметь свою собственную копию поля в своем собственном пространстве / кеше локальной памяти, если поле не было отмечено volatile
что заставляет поле быть окруженным барьером памяти, который вызывает синхронизацию памяти при каждом доступе / обновлении.
Без volatile
, любые обновления и читает на static
поле будет внесено в локальное хранилище потока и обновлено только тогда, когда поток пересекает барьер памяти. Без барьеров памяти нет никаких гарантий относительно порядка операций с данными и когда обновления будут переданы другим потокам.
Вот достойная страница о модели памяти Java и хороший обзор некоторых проблем.
Есть одна копия static
переменная для загрузчика классов, который загрузил этот класс. Это более или менее означает процесс, однако вы должны знать о разнице.
Например, когда два веб-приложения объединяют один и тот же класс, класс будет загружен дважды, таким образом, имея две копии одного и того же класса. static
поле.
Если вам нужна переменная, имеющая независимое значение на основе потока, посмотрите на ThreadLocal
,
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html
Переменные класса (статические поля) Переменная класса - это любое поле, объявленное с модификатором static; это говорит компилятору, что существует ровно одна копия этой переменной, независимо от того, сколько раз был создан экземпляр класса. Поле, определяющее количество передач для конкретного вида велосипеда, может быть помечено как статическое, поскольку концептуально одинаковое количество передач будет применяться ко всем экземплярам. Код статический int numGears = 6; создаст такое статическое поле. Кроме того, можно добавить ключевое слово final, чтобы указать, что число передач никогда не изменится.
Потоки не имеют никакого отношения к этому. (С другой стороны, это делает загрузчик классов. Если вы используете в своем приложении несколько загрузчиков классов, вы, вероятно, находитесь в точке, где вы это понимаете).
Проблема с многопоточностью заключается в следующем: представление на 10000 футов памяти Java состоит в том, что существует один единственный фрагмент памяти, который используется всеми классами, всеми объектами, всеми загрузчиками классов и всеми потоками в работающей JVM - что бы это ни было доступно из одного места в коде, доступно везде (с соответствующей ссылкой). Единственным исключением являются регистры и стек выполнения, которые концептуально основаны на потоках.
Это работает чертовски хорошо с одним процессором, где потоки по очереди исполняются в одном наборе регистров, ALU и т.п. Но большинство современных компьютеров имеют несколько процессоров, поэтому несколько потоков могут выполняться буквально одновременно. Если бы все эти процессоры ссылались на одну и ту же физическую память, то (исходя из опыта реальной жизни) вы могли бы получить улучшение производительности примерно в 1,5 раза с 4 процессорами, и оно бы выродилось.
Таким образом, используется "кеш", так что каждый процессор имеет свою собственную маленькую частную копию битов и кусков большей памяти. Большую часть времени процессоры обращаются к совершенно разным областям памяти, поэтому это работает нормально, но иногда (например, при работе с каким-либо "общим" объектом) они должны "бороться" за один и тот же набор байтов.
Решение состоит в том, чтобы установить протоколы так, чтобы никакие два процессора не пытались изменить одни и те же места хранения в одно и то же время (или почти одно и то же), и гарантировать, что один из измененных процессоров будет "сброшен" в основное хранилище, а другие процессоры будут осведомлены о Изменения и рекомендуется перезагрузить их вид измененных данных.
Но это (невероятно) неэффективно делать это после каждой операции (и, откровенно говоря, разработчики оборудования в значительной степени избежали этой проблемы и перенесли большую часть этой работы на программное обеспечение, чем это, вероятно, оправдано). Таким образом, схемы используются так, что сброс и перезагрузка данных происходит только на определенных "границах" или при выполнении определенных специальных типов ссылок.
Обратите внимание, что все это не имеет абсолютно никакого отношения к тому, является ли переменная "статической" или нет, и на самом деле это не имеет отношения к тому, являются ли объекты "неизменяемыми". Это присуще современной многопроцессорной аппаратной архитектуре в сочетании с моделью потоков Java.
Как SLaks предложил один на JVM, но имейте в виду, что два потока, удерживающие блокировку на одной статической ссылке, будут блокировать друг друга, потому что существует только одна такая ссылка на vm. Но они не будут блокировать потоки, ссылающиеся на переменные экземпляра.
Фактически переменная класса соответствует классу, в ней используется только одна память, поэтому изменения, вносимые каждым экземпляром класса, могут изменить переменную класса.