2013-04-13 5 views
0

Потому что он всегда печатает «3». Не требуется синхронизация? Я тестирую эту простую вещь, потому что у меня возникают проблемы с реальной проблемой с несколькими потоками, что плохо иллюстрирует проблему, потому что она большая. Это упрощенная версия, чтобы продемонстрировать ситуацию.Почему эта нить безопасна?

class Test { 

public static int count = 0; 

class CountThread extends Thread { 

    public void run() 
    { 
     count++; 
    } 
} 

public void add(){ 
    CountThread a = new CountThread(); 
    CountThread b = new CountThread(); 
    CountThread c = new CountThread(); 

    a.start(); 
    b.start(); 
    c.start(); 

    try { 
     a.join(); 
     b.join(); 
     c.join(); 
    } catch (InterruptedException ex) { 
     ex.printStackTrace(); 
    } 

} 
public static void main(String[] args) { 

    Test test = new Test(); 
    System.out.println("START = " + Test.count); 

    test.add(); 

    System.out.println("END: Account balance = " + Test.count); 
} 
+1

Положите петлю вокруг «основного» кода, и вы увидите, что это не так. – Chan

ответ

2

Кажется мне, как count++ достаточно быстро, чтобы закончить, пока вы не вызовите «Run» для другого класса. Таким образом, в основном он работает последовательно.
Но, если это был пример реальной жизни, и два разных потока использовали CountThread параллельно, то да, у вас была бы проблема синхронизации.

Чтобы убедиться в том, что вы можете попробовать напечатать несколько пробных вывода до подсчета ++ и после, то вы увидите, если b.start() является вызовом count++ до того a.start() закончена. То же самое для c.start().

Рассмотрите использование вместо AtomicInteger, который лучше, чем синхронизации, когда это возможно -

incrementAndGet приращений
public final int incrementAndGet()
атомарно по одному текущему значению.

+0

Печать изменит порядок выполнения потока из-за синхронизации ввода-вывода или печати. Будет очень сложно доказать, что вы говорите. Хотя это и верно. – Gray

+0

Если я использую synchronized void run(), мне все еще нужно использовать AtomaticInteger()? – user697911

+0

Нет, но лучше использовать AtomicInteger, так как это даст более быстрые результаты. – danieln

0

Это не потокобезопасность, потому что выход правильный. Создание потока вызывает много накладных расходов на стороне ОС, и после этого следует ожидать, что эта одна строка кода будет выполнена в течение одного таймлиса. Он не является потокобезопасным любым способом, просто недостаточно потенциальных конфликтов, чтобы фактически вызвать его.

0

Это не потолочный сейф.

Это был просто короткий путь, чтобы иметь измеримый шанс показать проблему. Рассмотрите возможность подсчета на большее число (1000000?) В run, чтобы увеличить вероятность того, что 2 операции над несколькими потоками будут перекрываться.

Также убедитесь, что ваша машина не одноядерный процессор ...

4

Потому что он всегда печатает «3». Не требуется синхронизация?

Это не потокобезопасность, и вам просто повезло. Если вы запустите это 1000 раз или на разных архитектурах, вы увидите другой вывод - т. Е. Не 3.

Я бы предложил использовать AtomicInteger вместо статического поля ++, который не является .

public static AtomicInteger count = new AtomicInteger(); 
... 
public void run() { 
    count.incrementAndGet(); 
} 
... 
+0

Даже если я использую AtomicInteger, мне все равно придется использовать статику, не так ли? – user697911

+1

Вы можете передать счетчик в потоки в качестве аргумента для 'CountThread'. «Статический» - это простой способ для всех потоков ссылаться на один и тот же счетчик @ user697911. – Gray

+1

Не могли бы вы объяснить, что не так с ответом? – Gray

1

Этот код не является поточно-:

public static int count = 0; 

class CountThread extends Thread { 

    public void run() 
    { 
     count++; 
    } 
} 

Вы можете запустить этот код в миллион раз на одной системе, и это может пройти каждый раз. Это не значит, что он потокобезопасен.

Рассмотрим систему, в которой значение в count копируется в несколько кэшей процессоров. Все они могут быть обновлены независимо друг от друга, прежде чем что-то заставляет один из кэшей копироваться обратно в основную оперативную память.Учтите, что ++ не является атомной операцией. Порядок чтения и записи count может привести к потере данных.

Правильный способ реализации этого кода (с помощью Java 5 и выше):

public static java.util.concurrent.atomic.AtomicInteger count = 
           new java.util.concurrent.atomic.AtomicInteger(); 

class CountThread extends Thread { 

    public void run() 
    { 
     count.incrementAndGet(); 
    } 
} 
0

Чтобы сделать класс поточно-либо сделать подсчет volatile, чтобы заставить заборы памяти между потоками, или использовать AtomicInteger или переписывание как это (предпочтение):

class CountThread extends Thread { 

    private static final Object lock = new Object(); 

    public void run() 
    { 
     synchronized(lock) { 
      count++; 
     } 
    } 
} 
Смежные вопросы