Java синхронизируется на объекте
У меня к тебе большой вопрос. Как синхронизировать два разных метода из одного и того же класса, чтобы заблокировать один и тот же объект? Вот пример:
public class MyClass extends Thread implements Observer{
public List<AnotherClass> myList = null;
public MyClass(List<AnotherClass> myList){
this.myList = myList;
}
public void run(){
while(true){
//Do some stuff
myList.add(NotImportantElement);
}
}
public void doJob{
for(int i=0; i<myList.size; i++){
ElementClass x = myList.get(i);
//Do some more stuff
}
}
}
Вопрос в том, как я могу остановить run() от доступа к myList при выполнении doJob и наоборот?
Представьте себе: я запускаю тему и начинаю добавлять элементы в свой список. В случайный момент я вызываю doJob() из другого класса, который содержит ссылку на мой поток.
Как мне сделать блокировку? Спасибо!
LE
Хорошо, я понял концепцию блокировки, но теперь у меня есть другой вопрос.
Предположим, у меня есть класс с public static myList
и только один экземпляр этого класса. Из этого экземпляра я создаю n
случаи Thread
которые берут каждый элемент этого списка и делают с ним что-то еще.
Теперь, в определенный момент, myList
обновляется. Что происходит с теми потоками, которые уже обрабатывали элементы myList? Как я должен заблокировать доступ на myList
при обновлении?
5 ответов
ПРИМЕЧАНИЕ. Этот код предполагает, что у вас есть только один экземпляр MyClass. в соответствии с вашим постом это звучит как случай.
public class MyClass extends Thread implements Observer{
private List<AnotherClass> myList = null;
private Object lock = new Object();
public MyClass(List<AnotherClass> myList){
this.myList = new ArrayList(myList);
}
public void run(){
while(true){
//Do some stuff
synchronized(lock) {
myList.add(NotImportantElement);
}
}
}
public void doJob{
synchronized(lock) {
for(int i=0; i<myList.size; i++){
ElementClass x = myList.get(i);
//Do some more stuff
}
}
}
}
РЕДАКТИРОВАТЬ: Добавлено создание копии списка, чтобы внешние объекты не могли изменить список в соответствии с JB Nizet
РЕДАКТИРОВАТЬ 2: Сделал переменные закрытыми, чтобы никто другой не мог получить к ним доступ
Вы можете:
- Объявить оба
run
а такжеdoJob
synchronized
, Это будет использоватьthis
как замок; - Объявить список как
final
и синхронизировать на нем. Это будет использовать список в качестве блокировки. Объявление поля блокировки какfinal
это хорошая практика. Таким образом, некоторые методы вашего класса могут синхронизироваться на одном объекте, в то время как другие методы могут использовать другой объект для синхронизации. Это уменьшает конкуренцию за блокировку, но увеличивает сложность кода; - Ввести явное
java.util.concurrent.locks.Lock
переменная и использовать ее методы для синхронизации. Это улучшит гибкость кода, но также увеличит сложность кода; - Не делайте явной синхронизации вообще, а вместо этого используйте некоторую поточно-ориентированную структуру данных из JDK. Например,
BlockingQueue
или жеCopyOnWriteArrayList
, Это уменьшит сложность кода и обеспечит безопасность потоков. - Используйте синхронизацию для чтения / записи в
volatile
поле. Смотрите это ТАК сообщение. Это обеспечит безопасность, но значительно увеличит сложность. На второй мысли, не делай этого:)
Объявите оба метода как synchronized
чтобы заблокировать каждый экземпляр, или использовать synchronized(this){...}
блокировать, чтобы сделать блокировку только на текущий экземпляр.
Вы можете добавить
синхронизированный
ключевое слово для обоих методов ИЛИ использовать
synchronized(Myclass.class) {
}
Первый по существу использует объект Myclass.class, но он не так детализирован, как последний.
synchronized(myList) {
// do stuff on myList
}
Специальная документация: внутренние блокировки и синхронизация
Тем не менее, я призываю вас использовать потокобезопасную параллельную структуру данных для того, чего вы хотите достичь, чтобы избежать синхронизации себя и получить (намного) лучшую производительность: обзор одновременных пакетов