Šta je novo?

Java multithread problem

  • Začetnik teme Začetnik teme Himo
  • Datum pokretanja Datum pokretanja

Himo

Slavan
Učlanjen(a)
08.01.2008
Poruke
36
Poena
309
Pozdrav! 🙂

Ovako, imam 5 threadova koji vrse web scraping i upisuju rezultate u bazu podataka.
Citao sam na netu ponesto o sinhronizaciji ali daleko od toga da razumem 😀
Funkcija u objektu koji se povezuje na bazu mi je synchronized.
Ne znam koliko sam jasan...

Program ili radi savrseno ili mi izbaci mysqlexception za duplikat primary key...
Evo koda iz klase za konekciju...
Kod:
public class connectDatabase {

    private String url;
    private String username;
    private String password;
    private Vector data;
    private Vector car;
    private int aktuelnaStrana;
    Connection con=null;
    Statement stmt=null;
    ResultSet rs=null;

    
    public connectDatabase(String url, String username, String password, Vector data, int aktuelnaStrana) 
    {
        this.url=url;
        this.username=username;
        this.password=password;
        this.data=data;
        this.aktuelnaStrana=aktuelnaStrana;
    } 
    public synchronized void connect()
    {
        try
        {    
            String name = Thread.currentThread().getName();
            System.out.println(name + ":accessed...");

            Class.forName("com.mysql.jdbc.Driver").newInstance();
            con=DriverManager.getConnection(url,username,password);
            stmt=con.createStatement(); 
            int id;
            //proveravamo da li postoji upisa u bazi (id je primary key), nalazimo zadnji id i nastavljamo od njega
                String upit="select id from cars where Id is NOT NULL";
                rs=stmt.executeQuery(upit);
                
                if(!rs.next())
                {
                    //nema upisa, tj. id == null
                    id=0;
                }
                else
                {
                    upit="select Max(Id) from cars";
                    rs=stmt.executeQuery(upit);
                    rs.next();
                    id=Integer.valueOf(rs.getString(1));
                    id++;                   
                }
                aktuelnaStrana++;
            //System.out.println("Upisujemo rezultate "+aktuelnaStrana+" strane u bazu...");    
            for(int i=0;i<data.size();i++)
            {
                car=new Vector();
                car=(Vector)data.elementAt(i);
                String marka=car.elementAt(1).toString();
                String model=car.elementAt(2).toString();
                String godiste=car.elementAt(3).toString();
                String url=car.elementAt(6).toString();
                String slika=car.elementAt(5).toString();
                String kilometraza=car.elementAt(4).toString();
                String cena=car.elementAt(0).toString();
                //public static final String EURO = "\u20AC";
                 
                cena=cena.replace("€","EUR");
                upit="insert into cars (Id,marka,cena,model,godiste,kilometraza,glavnaSlika,urlDetails) values ('"+id+"','"+marka+"','"+cena+"','"+model+"','"+godiste+"','"+kilometraza+"','"+slika+"','"+url+"')";
                stmt.executeUpdate(upit);
                id++;
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}
Threadovi se konektuju praveci instancu objekta i koriste funkciju connect() .
Ako treba, postavicu i stackTrace error-a i kod klase sa thread-ovima.
Bio bih veoma zahvalan ako bi me neko usmerio u pravom smeru.
Pozdrav i hvala!
 
Poslednja izmena od urednika:
Ne znam mnogo o konkuretnom pristupu bazi, ali sta bi ja napravio, je statica konekcija na bazu.

I red cekanja sa monitorom za pristup tom statickom objektu.

U principu, Thread kada treba da pristupi bazi, proveri da li neko pristupa trenutno konekciji, ako pristupa, ceka nad tim objektom.

Kada zavrsis pristup odradis notify() ili notifyAll().
 
Ne znam mnogo o konkuretnom pristupu bazi, ali sta bi ja napravio, je statica konekcija na bazu.

I red cekanja sa monitorom za pristup tom statickom objektu.

U principu, Thread kada treba da pristupi bazi, proveri da li neko pristupa trenutno konekciji, ako pristupa, ceka nad tim objektom.

Kada zavrsis pristup odradis notify() ili notifyAll().

Hvala na odgovoru.
Moze neki osnovni primer sa kodom, ako imas vremena da iskucas? 🙂
 
Hvala na linku, bacam se na strebanje 🙂
 
Nisam bas Java expert, ali mislim da synchronized ogranicava pristup funkciji iz drugih threadova ali samo za tu instancu. Npr. ako imas
connectDatabase A;
i iz threada T1 pozoves A.connect() a zatim iz threada T2 pozoves A.connect() synchonized ce automatski izblokirati poziv iz T2, sve dok je poziv iz T1 ne zavrsi.

ai ako imas:

connectDatabase A, B;
i iz threada T1 pozoves A.connect() a iz threada T2 pozoves B.connect() oba ce raditi bez problema. Problem je u funkciji connect koja pristupa bazi i koja koristi nesto sto verovatno nije thread-safe.

Trebalo bi da imas jednu instancu objekta pa da svi threadovi koriste tu jednu jedinu instancu.

http://docs.oracle.com/javase/1.3/docs/guide/jdbc/spec/jdbc-spec.frame9.html

^procitaj boldovano.
 
Pa da jedna instanca objekta sql klase.
Sad pitanje je da li on podrzava konkurentan pristup? Ne znam nisam radio sa tim.
 
Hvala Vam svima na odgovorima.
Ja sam greskom pravio novu instancu objekta za povezivanje na mysql bazu u svakom thread-u, ispravio sam to i sad pri kreiranju svakog treda saljem referencu na jedan jedini objekat za povezivanje na bazu i sinhronizacija radi.
Pozdrav!
 
Ako ćeš tokom izvršavanja programa da imaš konstantan broj thread-ova, koji će samo da izvršavaju neku petlju u kojoj će da pristupaju bazi ne treba ti nikakva sinhronizacija među thread-ovima, samo napravi u svakom thread-u novi objekat (konekciju). Baza podataka će sa svoje strane da reši kako da opsluži više konekcija. (to je jedna od glavnih stvaki u teoriji baza podataka)
 
Kao sto rece Ivan, nije posao aplikacionog programera da sinhronizuje bazu podataka tako sto ce stavljati lokove na pristup konekciji. To je reseno u okviru DBMS-a, i jedan je od osnovnih njegovih zadataka. Resenje tvog problema se sastoji u tome da podesis DBMS po svojoj zelji i eventualno da ubacis izmedju neki connection pool (ukoliko ces imati mnogo konekcija na bazu).

Kad pricamo o podesavanju DBMS-a, ono na sta konkretno mozes da utices oko sinhronizacije u bazi je da podesis transaction isolation level (proguglaj o tome, veoma je bitno). Takodje, u zavisnosti koji DBMS koristis, mozes da koristis neke specijalne forme u upitima kako bi sinhronizovao pristup odredjenim redovima u tabeli. Primer toga je "SELECT...FOR UPDATE" forma u okviru mysql-a.

Dakle, zakljucak je da ne treba ti na nivou aplikacije da radis stvari koje su u domenu DBMS-a.
 
Moguće rešenje za izbegavanje duplicate key-a je korišćenje sekvence u bazi.
 
Posto koristi mysql, verovatno ce mu biti dovoljno da podesi autoincrement za primarni kljuc, posto mislim da mysql ne podrzava sekvence u formi u kojoj ih podrzavaju neke druge baze, nego se taj problem resava preko autoincrement polja.
 
Koliko ja znam najčešći lek za kolizije ključeva kod paralelnih operacija ili spajanja tabela je da se kao ključ koristi 128 bitni random generisan broj, odnosno GUID, koji se obično predstavlja kao string od 32 hex cifre. Neke baze imaju ovu stvar, ne znam za MySQL.
 
Ne treba mu to ovde, autoincrement na primarnom kljucu ce odraditi posao, a i performanse ce biti bolje.
 
"select id from cars where Id is NOT NULL"
"select Max(Id) from cars"

Ovo su dva nepotrebna upita pri svakoj tvojoj "transakciji", stavi autoinc na ID i pusti bazi da misli o tome....

PS.
Dobronamjerna napomena - zasto je "godiste", "kilometraza" i "cena" string, te cena sadrzi oznaku valute u vrijednosti? Stavi to u numericki oblik, a valute u neku drugu referisanu tabelu pa vezi sa id...
 
Nazad
Vrh Dno