Методы экземпляров и безопасность потоков переменных экземпляра
Я хотел бы знать, имеет ли каждый экземпляр класса свою собственную копию методов в этом классе?
Допустим, у меня следующий класс MyClass
:
public MyClass {
private String s1;
private String s2;
private String method1(String s1){
...
}
private String method2(String s2){
...
}
}
Итак, если два разных пользователя создают экземпляр MyClass
, например:
MyClass instanceOfUser1 = new MyClass();
MyClass instanceOfUser2 = new MyClass();
Знает ли каждый пользователь в своем потоке копию методов MyClass
? Если да, переменные экземпляра являются потокобезопасными, если только методы экземпляра ими управляют, правильно?
Я задаю этот вопрос, потому что часто читаю, что переменные экземпляра не являются потокобезопасными. И я не понимаю, почему так должно быть, когда каждый пользователь получает экземпляр, вызывая оператор new
?
Ответы
Ответ 1
Каждый объект получает свою собственную копию переменных экземпляра класса - это переменные static
, которые разделяются между всеми экземплярами класса. Причина, по которой переменные экземпляра не обязательно являются потокобезопасными, заключается в том, что они могут быть одновременно изменены несколькими потоками, вызывающими несинхронизированные методы экземпляра.
class Example {
private int instanceVariable = 0;
public void increment() {
instanceVariable++;
}
}
Теперь, если два разных потока обращаются к increment
, то у вас есть гонка данных - instanceVariable
может увеличиваться на 1 или 2 в конце двух возвращаемых методов. Вы можете устранить эту гонку данных, добавив ключевое слово synchronized
в increment
или используя AtomicInteger
вместо int
и т.д., Но дело в том, что только потому, что каждый объект получает свою собственную копию экземпляра класса переменные не обязательно означают, что к переменным обращаются в потокобезопасном режиме - это зависит от методов класса. (Исключением являются final
неизменяемые переменные, к которым нельзя получить доступ небезопасным образом, не доходя до чего-то, похожего на сериализацию.)
Ответ 2
Проблемы с многопоточным доступом возникают в первую очередь со статическими переменными и экземплярами класса, к которому осуществляется доступ одновременно.
Вам не следует беспокоиться о методах в классе, но больше о полях (это означает, что они ограничены уровнем класса). Если существует несколько ссылок на экземпляр класса, разные пути выполнения могут пытаться получить доступ к экземпляру одновременно, вызывая непреднамеренные последствия, такие как условия гонки.
Класс - это в основном проект для создания экземпляра объекта. Когда объект создается, он получает место в памяти, к которому обращается ссылка. Если более чем один поток имеет дескриптор этой ссылки, он может вызвать случаи, когда экземпляр одновременно открывается, это приведет к тому, что поля будут обрабатываться обоими потоками.
Ответ 3
"Переменные экземпляра не являются потокобезопасными" - это утверждение зависит от контекста.
Это правда, если, например, вы говорите о сервлетах. Это связано с тем, что сервлеты создают только один экземпляр, и к нему обращаются несколько потоков. Поэтому в этом случае переменные экземпляра не являются потокобезопасными.
В приведенном выше упрощенном случае, если вы создаете новый экземпляр для каждого потока, ваши переменные экземпляра являются потокобезопасными.
Надеюсь, это ответит на ваш вопрос.
Ответ 4
A method
- не что иное, как набор инструкций. Какой бы нить ни называл этот метод, получите копию этих инструкций. После этого начинается выполнение. Метод может использовать локальные переменные, которые являются method and thread-scoped
, или могут использовать общие ресурсы, такие как статические ресурсы, общие объекты или другие ресурсы, которые visible across threads
.
Ответ 5
Существует множество ситуаций, когда экземпляр может быть доступен из нескольких классов. Например, если ваш экземпляр является статической переменной в другом классе, то все потоки будут делиться этим экземпляром, и вы можете столкнуться с большими неприятностями. Это только первый способ, который появляется у меня в голове...
Ответ 6
Каждый экземпляр имеет свой собственный набор переменных экземпляра. Как бы вы определили, имел ли каждый экземпляр отдельный "экземпляр" методов? Разве разница не была бы видна только путем изучения состояния переменных экземпляра?
Фактически нет, существует только одна копия метода, то есть набор команд, выполняемых при вызове метода. Но при выполнении метод экземпляра может ссылаться на экземпляр, на который он вызывается с зарезервированным идентификатором this
. Идентификатор this
относится к текущему экземпляру. Если вы не квалифицируете экземплярную переменную (или метод) с чем-то другим, подразумевается this
.
Например,
final class Example {
private boolean flag;
public void setFlag(boolean value) {
this.flag = value;
}
public void setAnotherFlag(Example friend) {
friend.flag = this.flag;
}
}
Существует только одна копия байтов, которые составляют инструкции VM для методов setFlag()
и setAnotherFlag()
. Но когда они вызывается, this
устанавливается в экземпляр, по которому произошел вызов. Поскольку для неквалифицированной переменной подразумевается this
, вы можете удалить все ссылки на this
в этом примере, и она все равно будет работать точно так же.
Однако, если переменная квалифицирована, например friend.flag
выше, можно ссылаться на переменные другого экземпляра. Вот как вы можете столкнуться с проблемой в многопоточной программе. Но, пока объект не "убегает" из одного потока, чтобы быть видимым другим, не о чем беспокоиться.