Пытаюсь разобраться с многопоточностью в java. Вот простенький код:
public class ThreadTest {
private int counter;
public static void main(String[] args) {
ThreadTest test = new ThreadTest();
test.increment();
}
public void increment(){
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0; i<10; i++){
counter++;
}
//System.out.println(counter);
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0; i<10; i++){
counter++;
}
//System.out.println(counter);
}
});
thread1.start();
thread2.start();
System.out.println(counter);
}
}
Я ожидаю увидеть после отработки двух потоков значение общей переменной, которое будет случайным, но максимум 20. Тем не менее в консоль постоянно выводится 0. При этом, если раскомментировать System.out.println в обоих потоках, то выводятся действительно случайные значения (например, 10 и 20, 20 и 20, 14 и 14). Почему последний System.out.println всегда выводит 0 и как сделать так, чтобы выводилась общая переменная после отработки двух потоков?
У вас есть поток и вы создаёте ещё два. При этом System.out.println(counter);
в основном потоке выполняется сразу после старта двух других (которые, скорей всего, ещё не успели даже запуститься). Чтобы дождаться выполнения другого потока нужно вызвать метод join()
.
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter);
Но это не главная проблема. Основная проблема тут в состоянии гонки (Data Race / Состояние гонки). Это не видно на цикле из 20 итераций, но сделаем до 10000 и уже вместо ожидаемых 20000 выводится разное число (13208, 10865, 13532 для 3 запусков у меня на машине). Состояние гонки происходит, потому что counter++;
не атомарная операция (Атомарные и неатомарные операции (java)).
Как это исправить? Обернуть нужный участок в synchronized
блок
public class Jclass {
private int counter;
final Object mutex = new Object();
...
...
public void increment() throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (mutext) {
counter++;
}
}
}
};
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter);
}
}
Либо использовать атомарные классы
public class Jclass {
private final AtomicInteger counter = new AtomicInteger();
...
...
public void increment() throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.getAndIncrement(); // эквивалент counter++
//counter.incrementAndGet(); // эквивалент ++counter
//counter.set(x); // эквивалент counter = x
// и другие методы
}
}
};
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.get());
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Не могу понять, почему в данном примере не получается работать с 2 потоками
У меня есть своя библиотека servicejar которую я подключаю через nexus
Пробую делать REST сервис на Spring Boot + Firebird 30