Dwa obiekty - jedna tablica

0

Witam!
Chcę napisać program w stylu konsument - klient korzystający z wątków.
Wiem, że jeszcze wątki nie działają, ale nie w tym problem (póki co).
Teraz jak tworzę dwa nowe obiekty Sprzedawce i Konsumenta, korzystają one z dwóch różnych tablic, a chodzi raczej o to, żeby korzystały z jednej. Pewnie jest to banalny problem, ale dopiero zaczynam pisać w ogóle obiektowo i w javie, więc ciężko jest mi się przestawić a chciałbym, żeby było to czysto obiektowo.

Oto co już mam:

//Produkt.java

import java.util.ArrayList;

public abstract class Produkt {

    public int elementy = 5;
    public int indeks = 0;
    ArrayList lista = new ArrayList();


    public synchronized String pobieranie() {
        if (indeks == 0)
            return "";
        else {
            int tmp = indeks;
            Object temp;
            indeks--;
            temp = lista.get(tmp);
            return (String)temp;
        }
    }

    public synchronized boolean wstawianie(String nazwa) {
        if (indeks == elementy)
            return false;
        else {
            lista.add(indeks, nazwa);
            indeks++;
            return true;
        }

    }

} 
//Konsument.java
public class Konsument extends Produkt implements Runnable {

    public void run() {
        for (int i = 0; i < 15; i++) { // dla wizualizacji efektu
            if (pobieranie().equals(""))
                System.out.println("Lista pusta");
            else
                System.out.println(pobieranie() + "to pobral konsument");
        }
    }

}
//Sprzedawca.java
import java.util.*;
public class Sprzedawca extends Produkt implements Runnable {
 
    Random r = new Random();

    public void run() {
    for (int i = 0; i < 15; i++) { // dla wizualizacji efektu
            String token = Long.toString(Math.abs(r.nextLong()), 36);
            if (wstawianie(token))
                System.out.println(token + "to wstawil sprzedawca");
            else
                System.out.println("Tablica pelna");
        }
    }
}
0

tak koncepcyjnie to konsument i sprzedawca jest klasy produkt :-P Oni powinni posiadać produkt, więc nie dziedziczyć. Wtedy w konstruktorach klasy Produkt i sprzedawca podajesz referncję do produktów

0
moskitek napisał(a)

Wtedy w konstruktorach klasy Produkt i sprzedawca podajesz referncję do produktów

A jak to w Javie zrobić? Próbowałem tak, że w Main tworzyłem tablice i podawałem ją jako konstruktor do Sprzedawcy i Konsumenta, ale to chyba nie robiło referencji tylko kopie i dalej nie działało. No ale wtedy tez musiał bym osobno jakoś zarządzać rozmiarem tablicy i aktualnym indeksem. Dlatego zrobiłem to dziedziczenie z Produktu myśląc, że istnieje jakiś mechanizm pozwalający na to dwóm obiektom.

0

jak przekazujesz obiekt jako argument, to zawsze jest referencja, jak chcesz kopie to musisz to samemu zaznaczyć. Druga sprawa że tablice w Javie posiadają takie coś jak length, ArrayList size, więc nie ma potrzeby samemu tworzyć dodatkowych indeksów. Jeżeli nie działało to coś źle robiłeś.

0

To robie to teraz tak:

//Main.java
public class Main {
    public static void main(String[] args) {
        ArrayList lista = new ArrayList();
        int roz = 5;
        int ind = 0;
        Sprzedawca s = new Sprzedawca(lista, ind, roz);
        Konsument k = new Konsument(lista, ind, roz);
        s.run();
        k.run();
    }
}
//Sprzedawca.java
public class Sprzedawca implements Runnable {
    private ArrayList lista;
    private int indeks;
    private int elementy;
    Sprzedawca (ArrayList array, int ind, int elem) {
        this.lista = array;
        this.indeks = ind;
        this.elementy = elem;
    }
    
    Random r = new Random();
    
    public synchronized boolean wstawianie(String nazwa) {
        if (indeks == elementy)
            return false;
        else {
            lista.add(indeks, nazwa);
            indeks++;
            return true;
        }
    }

    public void run() {
    for (int i = 0; i < 15; i++) {
            String token = Long.toString(Math.abs(r.nextLong()), 36);
            if (wstawianie(token))
                System.out.println(token + "to wstawil sprzedawca");
            else
                System.out.println("Tablica pelna");
        }
    }
}
//Konsument.java
public class Konsument implements Runnable {

    ArrayList lista;
    int indeks;
    int elementy;
    
    Konsument (ArrayList array, int ind, int elem) {
        this.lista = array;
        this.indeks = ind;
        this.elementy = elem;
    }

public synchronized String pobieranie() {
        if (indeks == 0)
            return "";
        else {
            int tmp = indeks;
            Object temp;
            indeks--;
            temp = lista.get(tmp);
            return (String)temp;
        }
    }

    public void run() {
        for (int i = 0; i < 15; i++) {
            if (pobieranie().equals(""))
                System.out.println("Lista pusta");
            else 
                System.out.println(pobieranie() + "to pobral konsument");
        }
    }
}

I wciąż konsument widzi tylko listę pustą. Co robie nie tak?

0

Utworzyłeś w klasie Main pustą listę (typu ArrayList) i przekazałeś ją do Konsumenta, zatem Konsument widzi cały czas pustą listę. Ja bym robił taK

//Main.java
public class Main {
    ArrayList lista = new ArrayList();
    public static void main(String[] args) {
        int roz = 5;
        int ind = 0;
        Sprzedawca s = new Sprzedawca(this, ind, roz);
        Konsument k = new Konsument(this, ind, roz);
        s.run();
        k.run();
    }
}
//Sprzedawca.java
public class Sprzedawca implements Runnable {
    private Main parent;
    private int indeks;
    private int elementy;
    Sprzedawca (Main parent, int ind, int elem) {
        this.parent = parent;
        this.indeks = ind;
        this.elementy = elem;
    }
   
    Random r = new Random();
   
    public synchronized boolean wstawianie(String nazwa) {
        if (indeks == elementy)
            return false;
        else {
            parent.lista.add(indeks, nazwa);
            indeks++;
            return true;
        }
    }

    public void run() {
    for (int i = 0; i < 15; i++) {
            String token = Long.toString(Math.abs(r.nextLong()), 36);
            if (wstawianie(token))
                System.out.println(token + "to wstawil sprzedawca");
            else
                System.out.println("Tablica pelna");
        }
    }
}
//Konsument.java
public class Konsument implements Runnable {

    Main parent;
    int indeks;
    int elementy;
   
    Konsument (Main parent, int ind, int elem) {
        this.parent = parent;
        this.indeks = ind;
        this.elementy = elem;
    }

public synchronized String pobieranie() {
        if (indeks == 0)
            return "";
        else {
            int tmp = indeks;
            Object temp;
            indeks--;
            temp = parent.lista.get(tmp);
            return (String)temp;
        }
    }

    public void run() {
        for (int i = 0; i < 15; i++) {
            if (pobieranie().equals(""))
                System.out.println("Lista pusta");
            else
                System.out.println(pobieranie() + "to pobral konsument");
        }
    }
}
0
bogdans napisał(a)

Utworzyłeś w klasie Main pustą listę (typu ArrayList) i przekazałeś ją do Konsumenta, zatem Konsument widzi cały czas pustą listę.

No ale najpierw referencje do listy przekazałem Sprzedawcy który zapełnił ją stringami, po czym Konsument miał pobrać te Stringi. A tak się nie dzieje. Czy ja coś źle rozumiem z tymi referencjami czy o co chodzi?

0

w metodzie pobieranie masz

if (indeks == 0)
            return "";

A indeks zawsze tu bedzie rowny 0, bo go nie zmieniasz a inicjujesz go zerem.
Pamietaj, ze typy proste (np int) nie sa przekazywane przez referencje tylko przez wartosc, wiec w momencie gdy tworzysz zmienna int ind = 0 a potem przekazujesz ja do konstruktora sprzedawcy a potem konsumenta, to kopiujesz ta zmienna i od tej pory oba obiekty maja wlasna ta zmienna.

Jesli chcesz miec zmienne opisujace cos to trzymaj je przy tym czyms, w tym przypadku najlepiej zrob osobna klase ktora ma ta liste i te 2 zmienne. Zreszta to i tak nie ma duzego sensu, bo zlej struktury probujesz tu uzyc, najlepiej uzyj kolejki i nie potrzebujesz wtedy pamietac zadnych info o niej.
Dalej, dodajesz do listy uzywajac indexu, co nie ma sensu, bo i tak dodajesz na sam koniec.

0

Do bezpiecznej i prostej realizacji schematu producent/konsument służy ta klasa:
http://java.sun.com/javase/6/docs/api/java/util/concurrent/ArrayBlockingQueue.html

0

Dobra, tablica już działa, wątki również. Jeszcze ostatnia rzecz. Chce zrobić tak, że jeśli tablica jest pełna to Sprzedawca zasypia czekając na znak od konsumenta. Zrobiłem to tak, jednak nie działa:

//Sprzedawca.java
public class Sprzedawca extends Thread {

    Produkt produkt;
    Sprzedawca (Produkt produkt) {
        this.produkt = produkt;
        start();
    }

    Random r = new Random();

    public boolean wstawianie(String nazwa) {
        synchronized(this) {
            if (produkt.indeks == produkt.elementy) {
                return false;
            }
            else {
                produkt.lista.add(produkt.indeks, nazwa);
                produkt.indeks++;
                return true;
            }
        }
    }

    public @Override void run() {
    while(true) {
            String token = Long.toString(Math.abs(r.nextLong()), 36);
            if (wstawianie(token)) {
                System.out.println(token + "to wstawil sprzedawca");
                yield();
            }
            else {
                System.out.println("Tablica pelna");
                synchronized(this) {
                    try {
                        wait();
                    }
                    catch (InterruptedException e) {
                        
                    }
                }
            }
        }
    }
}
//Konsument.java
public class Konsument extends Thread {

    Produkt produkt;
    Sprzedawca sprzedawca;
    Konsument (Produkt produkt, Sprzedawca sprzedawca) {
        this.produkt = produkt;
        this.sprzedawca = sprzedawca;
        start();
    }

    public String pobieranie() {
        synchronized(this) {
            if (produkt.indeks == 0)
                return "Nic nie";
            else {
                produkt.indeks = produkt.indeks - 1;           
                return (String)produkt.lista.get(produkt.indeks);
            }
        }
    }

    public @Override void run() {
        while(true) {
            synchronized(this) {
                if (produkt.indeks == produkt.elementy)
                    sprzedawca.notify();
                System.out.println('\t' + pobieranie() + " pobral konsument");
                yield();                
            }
        }
    }
}

jakieś podpowiedzi, dlaczego to nie chodzi? Daje mi taki bład jak tablica jest pełna:

7jwtigp63vujto wstawil sprzedawca
Tablica pelna
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
        at java.lang.Object.notify(Native Method)
        at lista6.Konsument.run(Konsument.java:28)
//28 linijka
sprzedawca.notify();
0

Synchronizuj dostęp do produktu

synchronized(produkt) { 
... 
}

notify i wait także na produkcie

0

Witam ponownie. Powróciłem do tematu wątków w javie. Synchronizacja na kolejce działała dobrze, konsument i sprzedawca byli zadowoleni.
Teraz jednak muszę to zrobić na zwykłej liście i synchronizować to za pomocą mutexa. Oto co mam. I nie mam pojęcia dlaczego tylko ten kod dodaje jednego stringa do listy po czym się zatrzymuję.

EDIT: Ech, kłania się czytanie dokumentacji... metoda tryAcquire() automatycznie zajmuje miejsce w semaforze, jeśli jest ono wolne, wiec nie trzeba po niej wywoływać Acquire(). Zostawiam mój błąd dla potomnych ku przestrodze.

Bufor.java
import java.util.concurrent.Semaphore;

public class Bufor {

    private Semaphore s;
    String[] bufor;
    int i;

    Bufor (int size) {
        this.s = new Semaphore(1);
        this.bufor = new String[size];
        this.i = 0;
        s.release();
    }
    
    public synchronized boolean  wstawianie (String nazwa) throws InterruptedException {
        if (!(i < bufor.length)) {
            try {
                System.out.println("\t### Sprzedawca: Bufor pelny ZzZz");
                wait();
            } catch (InterruptedException e) { }
        }
        if (s.tryAcquire()) {
            try { s.acquire(); } catch (Exception e) {} // niepotrzebne
            System.out.print("Dodany indeks: " + i + " ");
            bufor[i] = nazwa;
            i++;
            s.release();
            notifyAll();
            return true;
       } else
            try { wait(); } catch (Exception e) { }
        
        return false;
    }

    public synchronized String pobieranie() {
        try {
            if (i == 0) {
                System.out.println("### Klient: Bufor pusty ZzzZz");
                wait();
            }                    
            if (s.tryAcquire()) {
                s.acquire(); // niepotrzebne
                i--;                     
                s.release();                        
                notifyAll();
                return bufor[i];
            }
        }
        catch (InterruptedException e) {}
        s.release();
        notifyAll();
        return "Nic ";        
    }
}
Sprzedawac.java

import java.util.*;

public class Sprzedawca extends Thread {        

    Bufor bufor;
    Sprzedawca (Bufor produkt) {
        this.bufor = produkt;               
        start();                            
    }

    Random r = new Random();

    public @Override void run() {
        while(true) {
            String token = Long.toString(Math.abs(r.nextLong()), 36);   
            if(bufor.wstawianie(token))
                System.out.println(token + " to wstawil sprzedawca");
            yield();
        }
    }
}
Konsument.java

public class Konsument extends Thread {     
    Bufor bufor;

    Konsument (Bufor produkt) {         
        this.bufor = produkt;
        start();                       
    }

    public @Override void run() {       
        while(true) {
            try {
                System.out.println("\t$$$" + bufor.pobieranie() + " pobral konsumen");
                sleep(0);
                yield();
            }                           
            catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

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