използване на променливи на атомни променливи

Използването на volatile върху променлива намалява риска от грешка в последователността на паметта (моля, поправете ме, ако това разкрива някои пропуски в моето разбиране на всяка подходяща концепция). Така че в следващия пример, въпреки че променливата c1 е непостоянна, все пак възникването на грешка в постоянството на паметта води до това, че c1 става 15 или понякога 14 в изхода, а не правилния изход 16.

class Lunch implements Runnable {

    private volatile long c1 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    public void inc1() {
       // synchronized(lock1) { c1 is volatile
            c1++;
       // }
    }

    public void run() {
        try {
            inc1();
            Thread.sleep(1000);
            inc1();
            Thread.sleep(1000);
            inc1();
            Thread.sleep(1000);
            inc1();
            inc1();
            Thread.sleep(1000);
            inc1();
            Thread.sleep(1000);
            inc1();
            Thread.sleep(1000);
            inc1();
        }
        catch(InterruptedException e) {
            return;
        }
    }
    public long value() {
        return c1;
    }
    public static void main(String args[]) throws InterruptedException {
        Lunch l = new Lunch();
       Thread t1 = new Thread(l);
       Thread t2 = new Thread(l);
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println(l.value());
    }
} 

person msk    schedule 03.09.2010    source източник
comment
Предположението тук е, че ++ е атомно. Проверете предположенията си!   -  person nos    schedule 03.09.2010
comment
(Малко се притеснявам, че използването на фразата намалява риска от ваша страна. Обикновено целта е да се гарантира, че изобщо няма риск.)   -  person Tom Hawtin - tackline    schedule 03.09.2010


Отговори (3)


Прав си. Тъй като ++ НЕ е атомарна операция, все още можете да получите противоречиви резултати, когато една нишка чете/увеличава/записва стойност едновременно с друга нишка.

Обмислете използването на AtomicInteger за случаи като този.

person Mike Q    schedule 03.09.2010
comment
Така че, ако заменя множество извиквания на increment() в run() с друга функция update(long c1) { this.c1 = c1 }, която е част от този клас. След това, тъй като c1 е непостоянен и ++ не е включен, резултатът трябва да бъде постоянно правилен? - person msk; 03.09.2010
comment
Или AtomicIntegerUpdater при някои обстоятелства, при които операциите зареждане-манипулиране-съхранение са редки. - person Tom Hawtin - tackline; 03.09.2010
comment
@msk би било правилно, тъй като във всеки момент всяка нишка, която чете c1, ще бъде гарантирано, че ще види актуалната стойност на c1. Полезността на информацията, която е зададена без да е предварително проверена, често е съмнителна. Обичайният случай на използване е булев флаг, при който една нишка сигнализира друга (като стоп флаг). Това е почти единственият случай, в който използвам volatile. - person Mike Q; 03.09.2010

Атомичността е само част от картината. Има и видимост. Ако стойността на енергонезависима (и несинхронизирана) променлива бъде променена, не е гарантирано, че другите нишки ще видят промяната навреме или изобщо.

person Péter Török    schedule 03.09.2010

Проверете http://www.javabeat.net/tips/169-volatile-keyword-in-java.html за разбиране какво прави volatile. Той избягва междинния кеш на нишката, но така или иначе може да има загубена актуализация от четене на нейната стойност и актуализиране в неатомна операция.

person helios    schedule 03.09.2010