#java #multithreading
#java #многопоточность
Вопрос:
Я пытаюсь написать простой код для последовательной печати чисел. Сценарий выглядит так
1
2
3
4
5
.. and so on
вот код, который я написал
public class CounterThread implements Runnable {
int counterNumber;
public CounterThread(int counterNumber) {
this.counterNumber = counterNumber;
}
@Override
public void run() {
System.out.println(this.counterNumber);
}
public static void main(String[] args) throws InterruptedException {
int number = Integer.parseInt(args[0]);
Thread[] listOfThreads = new Thread[number];
for(int i = 1; i <= number; i ){
CounterThread counterThread = new CounterThread(i);
Thread thread = new Thread(counterThread);
thread.start();
listOfThreads[i-1] = thread;
}
for(Thread thread : listOfThreads){
thread.join();
}
}
}
Я понимаю, что это очень плохая практика кодирования для программирования потоков, но я все еще на уровне новичка, пытаясь понять, как потоки ведут себя при синхронизации, а когда нет.
Выходные данные перемешаны, что и следовало ожидать, но не то, что требуется.
я не уверен, где я ошибаюсь, любая помощь очень ценится.
Комментарии:
1. Есть ли какие-либо другие требования? Вам не разрешено синхронизировать потоки — это единственное условие?
2. Что подразумевается под «отсутствием синхронизации»? В конечном счете вам понадобится какая-то синхронизация, но, возможно, то, что требует назначение, — это отсутствие использования
synchronized
ключевого слова.3. @Steyrix Да, мне не разрешено синхронизировать потоки, это единственное условие
4. @JoseMartinez да, вы правы,
synchronized
ключевое слово не используется
Ответ №1:
Вы можете оптимизировать свой код, написав это, это просто заставит основной поток ждать завершения текущего thread
выполнения.
for (int i = 1; i <= number; i ) {
CounterThread counterThread = new CounterThread(i);
Thread thread = new Thread(counterThread);
thread.start();
thread.join();
}
Однако это действительно странный фрагмент кода. В таких случаях, когда вам не разрешено использовать synchronized
многопоточную среду, лучшее, что вы можете сделать, это использовать volatile
ключевое слово или атомарные переменные. Оба подхода предназначены для выполнения всех операций записи и чтения в переменную atomic.
volatile int number = 1;
...
@Override
public void run() {
System.out.println(number );
}
...
for (int i = 1; i <= number; i ) {
CounterThread counterThread = new CounterThread();
Thread thread = new Thread(counterThread);
thread.start();
}
Но вы должны отметить, что volatile
это не дает 100% гарантии, что вы избежите состояния гонки. Особенно в этом случае, поскольку number
это не будет атомарной операцией. На самом деле это будет number = number 1
— одно чтение и одна запись.
Но с Atomic
увеличением будет атомарной операцией, и вы можете написать что-то вроде
AtomicInteger number = new AtomicInteger(0);
...
@Override
public void run() {
System.out.println(number.incrementAndGet());
}
Комментарии:
1. Спасибо за это решение и предоставление другого подхода с изменчивыми переменными, я обязательно рассмотрю это. очень признателен 🙂
2. @PrajwalKrishna добро пожаловать. Однако вы приняли другой ответ, который не так информативен, как мой, и также опубликован позже: (
Ответ №2:
вы можете просто вызвать join()
after start()
, потому что запуск метода в первом цикле вызывает его выполнение, и перед переходом к следующему циклу он уже выполнил свою работу, поэтому вам нужно вызвать его в первом цикле после запуска, чтобы убедиться, что он заканчивается перед запуском следующего:
метод join(), который позволяет одному потоку ждать, пока другой поток завершит свое выполнение. Если t является объектом потока, поток которого выполняется в данный момент, то t.join() удостоверится, что t завершается до того, как программа выполнит следующую инструкцию.
public class CounterThread implements Runnable {
int counterNumber;
public CounterThread(int counterNumber) {
this.counterNumber = counterNumber;
}
@Override
public void run() {
System.out.println(this.counterNumber);
}
public static void main(String[] args) throws InterruptedException {
int number = Integer.parseInt(args[0]);;
for (int i = 1; i <= number; i ) {
CounterThread counterThread = new CounterThread(i);
Thread thread = new Thread(counterThread);
thread.start();
thread.join();
}
}
}
Комментарии:
1. Обновите код, в этом нет необходимости, просто скопированный из пользовательского кода
2. Спасибо за это решение, оно сработало как шарм 🙂 Я хотел знать, что происходило, когда я присоединялся к другому циклу for ? был
run()
ли метод уже выполнен до объединения потоков?3. когда вы вызывали соединение во втором цикле, потоки уже запускались и завершали свою работу. итак, я добавил описание метода join(), вам нужно было вызвать join после вызова start, чтобы убедиться, что он заканчивается после запуска следующего потока
4. Что-то не так с этим решением. На самом деле это не выполняется в многопоточном … это решение использует один поток за раз. Я бы предположил, что было бы интереснее, чтобы потоки выполнялись одновременно.