Я пытаюсь запустить программу с 4 потоками. Я хотел бы знать о поведении переменных экземпляра в многопоточности. В этом мне нужно передать значение методу. Итак, я делаю так, чтобы передать значение и использовать его для дальнейшей обработки.
@Component
class ThreadExample implements Runnable
{
String file;
public void setFile(String file)
{
this.file = file;
}
@override
public void run()
{
readFile(file);
}
public void readFile(String fileName)
{
//logic for reading file
}
}
@component
class ThreadCall
{
@Autowired
ThreadExample ex;
public void testThread()
{
ExecutorService executor2 = Executors.newFixedThreadPool(4);
getFiles()
.stream()
.forEach(f-> {
ex.setFile(f);
executor2.submit(ex);
});
}
}
getFiles() API возвращает список имен файлов. Я пытаюсь сделать это в Spring.
Здесь одновременно будут работать 4 потока. Объект ex — это autowired, поэтому он будет создан только один раз.
Как повлияет значение переменной file?
Я думаю, что один поток попытается установить файл через setFile(file), и он будет изменен другим потоком, прежде чем использовать его в readFile.
Как побороть эту проблему? Как передать значение в многопоточности?
Будет ли решена моя проблема, если я сделаю «файл» как volatile?
Кажется, что существует логическое несоответствие между тем, что вы хотите сделать, и тем, как вы хотите это сделать. Когда у вас есть пул потоков, вы, как правило, хотите, чтобы было отправлено несколько новых потоков, а не один элемент, отправленный снова и снова.




Здесь:
@Component
class ThreadExample implements Runnable
{
String file;
public void setFile(String file)
И
Object ex is autowired so it will be instantiated only once.
Это не может работать. У вас есть:
executor2.submit(ex);
Вы передаете объект такой же нескольким задачам, которые должны делать что-то конкретное параллельно. Хуже того, вы вставляете разные значения в этот единственный объект и каким-то волшебным образом ожидаете, что каждая задача увидит именно то значение, которое вы хотели, чтобы она увидела. Что произойдет: эти вещи происходят в разных потоках, поэтому результат (вероятно) совершенно случайный, он же недетерминированный.
Короче говоря: когда у вас есть несколько «вещей», которые нужно сделать с течением времени, вы не можете использовать «одиночный» контейнер для отправки ваших параметров. volatile тут никак не поможет.
Ответ таков: чтобы это работало, ThreadExample не может быть "синглтоном", поэтому аннотация @Component, которая превращает его в синглтон, должна быть убрана. Если это «вступает в противоречие» с другими вашими дизайнерскими идеями, вам придется отступить и переработать свой дизайн.
Re, '...множественные "потоки"...' Вместо того, чтобы использовать пугающие кавычки, вы могли бы назвать их тем, чем они являются --- несколько задания, которые потенциально могут выполняться разными потоками.
@SolomonSlow Спасибо, хорошая мысль. Адаптировал ответ соответственно.
@GhostCat Одно сомнение, каждая переменная, кроме file, является локальной для метода readFile(). Итак, я думаю, что все локальные переменные метода создаются для каждого вызова, даже если объект ex является одноэлементным. Проблема только с file. Исправь меня.
Вы должны сделать ThreadExample в качестве прототипа bean-компонента.
@Component
@Scope("prototype")
class ThreadExample implements Runnable
{
String file;
public void setFile(String file)
{
this.file = file;
}
@Override
public void run()
{
readFile(file);
}
public void readFile(String fileName)
{
//logic for reading file
}
}
Затем вместо внедрения bean-компонента ThreadExample в ThreadCall вам нужно внедрить контекст приложения:
@Component
class ThreadCall
{
@Autowired
ApplicationContext context;
public void testThread()
{
ExecutorService executor2 = Executors.newFixedThreadPool(4);
getFiles()
.stream()
.forEach(f-> {
ThreadExample ex= context.getBean(ThreadExample.class);
ex.setFile(f);
executor2.submit(ex);
});
}
}
Когда вы вызываете этот код, в основном каждый ThreadExample будет новым bean-компонентом.
Спасибо за ответ. Что произойдет, если я Autowired другой объект в ThreadExample, который является одноэлементным, и он должен быть одноэлементным. Будет ли он вести себя как prototype?
Да, пока вы объявили ThreadExample в качестве прототипа и используете контекст приложения для получения его экземпляра, Spring предоставит вам новый объект для ThreadExample. Также во время создания этого нового объекта он проверит его зависимость, и если вы автоматически подключили одноэлементный компонент, он введет этот одноэлементный компонент в прототип компонента (в данном случае это ThreadExample). В моем последнем проекте у нас были валидаторы, которые были прототипом, и я вводил внутрь однотонные репозитории. Каждый раз, когда я запрашивал новый экземпляр валидаторов из контекста приложения, Spring также вводил репозитории.
«Объект ex автоматически подключается, поэтому он будет создан только один раз». Эх, не совсем. Точнее,
ThreadExample— это компонент, поэтому он будет создан только один раз.