Java синхронизируется по методу Не работает? - PullRequest
5 голосов
/ 15 августа 2010

Я экспериментирую с многопоточностью Java, используя синхронизацию с методом, сравниваемым с атомарными переменными (пакет java.util.concurrent.atomic).

Ниже приведены классы:

// Interface ICounter.java
        public interface ICounter {
            public void increment();
            public void decrement();
            public int value();
        }

// Class Counter.java
    public class Counter implements ICounter {
        private int c = 0;

        @Override
        public void increment() {
            c++;
        }

        @Override
        public void decrement() {
            c--;
        }

        @Override
        public int value() {
            return c;
        }
    }

// Class AtomicCounter.java
    import java.util.concurrent.atomic.AtomicInteger;

    public class AtomicCounter implements ICounter {
        private AtomicInteger c = new AtomicInteger(0);

        @Override
        public void increment() {
            c.incrementAndGet();
        }

        @Override
        public void decrement() {
            c.decrementAndGet();
        }

        @Override
        public int value() {
            return c.get();
        }

        public long getIncrement() {
            return c.incrementAndGet();
        }
    }

// Class MainProg.java
    public class MainProg {
        public static void main(String args[]) {
            ICounter counter = new AtomicCounter();
                    //ICounter counter = new SynchronizedCounter();
            Thread thread1 = new Thread(new CountRunner(counter));
            Thread thread2 = new Thread(new CountRunner(counter));

            thread1.start();
            thread2.start();
        }
    }

    class CountRunner implements Runnable {
        private ICounter counter;
        public CountRunner(ICounter counter) {
            this.counter = counter;
        }

        public void run() {
            while (true) {
                counter.increment();
                System.out.println(Thread.currentThread().getName() + " count=" + counter.value());
                System.out.println("-------------------");
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

Результаты выполнения Atomic или Synchronized не показывают, что целое значение переменной является поточно-ориентированным, например:

Thread-0 count=1
-------------------
Thread-1 count=2
-------------------
Thread-0 count=3
-------------------
Thread-1 count=4
-------------------
Thread-0 count=5
-------------------
Thread-1 count=6
-------------------
Thread-0 count=7
-------------------
Thread-1 count=8
-------------------
Thread-0 count=10
-------------------
Thread-1 count=10
-------------------

Из результатов, последние 2 строки показывают, что 2 потока обращались к одному и тому же значениюцелочисленная переменная класса счетчика.Возможно, я что-то здесь упускаю?

Спасибо!

Ответы [ 3 ]

4 голосов
/ 15 августа 2010

потому что ваши counter.increment() и System.out.println не одно атомное действие.

Thread 1               Thread2

increment

                       increment

                       System.out.println  // print 10

System.out.println  
// print 10 too
4 голосов
/ 16 августа 2010

Вы увеличиваете значение за один шаг, а затем получаете значение за другой шаг. Хотя каждый из этих отдельных шагов гарантированно будет атомарным с помощью нижележащего класса AtomicInteger, тот факт, что вы выполняете две отдельные операции, оставляет значение, которое вы видите в вашем операторе печати, во власти порядка выполнения потоков. *

Чтобы иметь возможность точно отображать значение, обновленное данным потоком, вам нужно как обновить, так и получить результирующее значение в одной операции, что делает ваш метод getIncrement(). Код, который даст вам ожидаемые результаты, будет выглядеть так:

int changedValue = counter.getIncrement();
System.out.println(Thread.currentThread().getName() + " count=" + changedValue);
2 голосов
/ 15 августа 2010

Что вы пропустили, так это то, что ваш класс AtomicCounter работает правильно, и наблюдаемое поведение происходит, потому что потоки переключаются между вызовами на .increment() и .value():

-------------------
Thread-0 increment -> value = 9
-------------------
Thread-1 increment -> value = 10
-------------------
Thread-0 print value <- 10
-------------------
Thread-1 print value <- 10
-------------------
...