pole volatile - dlaczego mój kod nie działa

0

Używam w wielowątkowym programie pola volatile po to, aby dane nie zostały uszkodzone.
Kod programu:

package threads;

import java.util.*;
import java.util.concurrent.locks.*;

/**
 *
 */
public class Main
{
    public Main()
    {
        //
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        Cash cash = new Cash(0);
        Thread thread1 = new Thread(new Adder(cash));
        Thread thread2 = new Thread(new Subtracter(cash));
        thread1.start();
        thread2.start();
    }   
}

/**
 *
 */
class Cash
{
    public Cash(double number)
    {
        this.number = number;
    }
    
    public String toString()
    {
        return String.valueOf(number);
    }
    
    public void setNumber(double number)
    {
        this.number = number;
    }
    
    public double getNumber()
    {
        return number;
    }
    
    private volatile double number;
}

/**
 *
 */
abstract class Worker implements Runnable
{
    public Worker(Cash cash)
    {
        this.cash = cash;
    }
    
    public void setCash(Cash cash)
    {
        this.cash = cash;
    }
    
    public Cash getCash()
    {
        return cash;
    }
    
    public void run()
    {
        try
        {
            for (int i = 0; i < 100; i++)
            {
                operation();
                Thread.sleep(1000);
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
    public abstract void operation();
    
    private Cash cash;
}

/**
 *
 */
class Adder extends Worker
{
    public Adder(Cash cash)
    {
        super(cash);
    }
    
    public void operation()
    {
        Cash cash = getCash();
        System.out.print("do " + cash);
        cash.setNumber(cash.getNumber() + 100);
        System.out.println(" dodano 100 i otrzymano " + cash.getNumber());
    }
}

/**
 *
 */
class Subtracter extends Worker
{
    public Subtracter(Cash cash)
    {
        super(cash);
    }
    
    public void operation()
    {
        Cash cash = getCash();
        System.out.print("od " + cash);
        cash.setNumber(cash.getNumber() - 100);
        System.out.println(" odjęto 100 i otrzymano " + cash.getNumber());
    }
}

Wydruk:

do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
do 0.0 dodano 100 i otrzymano 100.0
od 100.0 odjęto 100 i otrzymano 0.0
od 0.0 odjęto 100 i otrzymano -100.0 /////////////////////////////////////// tu się zaczynają problemy :(
do -100.0 dodano 100 i otrzymano 0.0
od 0.0 odjęto 100 i otrzymano -100.0
do -100.0 dodano 100 i otrzymano 0.0
od 0.0 odjęto 100 i otrzymano -100.0
do -100.0 dodano 100 i otrzymano 0.0
od 0.0 odjęto 100 i otrzymano -100.0
do -100.0 dodano 100 i otrzymano 0.0

Po drobnych przemyśleniach myślę, że pole volatile się w mojej sytuacji nie przyda. Jeśli mam rację to jak to rozwiązać w inny sposób?

0

synchronizacja.
metoda synchronized (jesli jest jedna)
ReentraintLock (jesli obiekt ma byc monitorem, czyli tylko jedna metoda wywolywana na raz)

pozdrawiam

0

A czy mógłbyś mi pokazać, jak powinien wyglądać zmieniony kod, bo mi coś nie wychodzi?

0
public synchronized void tylkaTaMetodeBedeWywolywal(){
   //i tu zmieniam stan obiektu
}
//lub
class MojMonitor{
   private ReentraintLock lock;
   private int bardzoWanzyInt;
   public void jednaZMetod(){
      lock.lock();
      try{
         bardzoWanzyInt++;
      }catch(Exception e){
          //handle
      }finally{
         lock.unlock()
      }
   }
   public void innaMetoda(){
      lock.lock();
      try{
         bardzoWanzyInt--;
      }catch(Exception e){
          //handle
      }finally{
         lock.unlock()
      }
   }
}

w Twoim przypadku Adder i Substracter moga dziedziczyc po Monitorze i odwolywac sie do protected lock'a
pozdrawiam

0

@eximius, a czy przez przypadek wersja z flagą blokującą nie jest niebezpieczna w wątkach? Jeżeli zmiana wykonywanego wątku nastąpi PO sprawdzeniu flagi i PRZED jej zmianą to otrzymasz efekt w postaci dwóch wątków wykonujących operację na tym samym teoretycznie zablokowanym polu. Ominiesz to przez synchronizację metody lock() jednak lepiej już synchronizować metodę główną.
Blokowanie lepiej sprawdza się gdy na polu można przeprowadzić kilka różnych operacji i część z nich może być przeprowadzona bo nie wymaga blokowania. Coś na wzór blokowania rekordów w RDB.

0

ke? to jest mutex przeciez

0

Ok mój błąd bo dokumentacji nie doczytałem. po prostu nigdy nie miałem okazji korzystać z tego roziwązania.

1 użytkowników online, w tym zalogowanych: 0, gości: 1