Avatar billede mora Nybegynder
11. januar 2004 - 05:10 Der er 11 kommentarer og
1 løsning

java.lang.IllegalMonitorStateException

Først hvordan det virker, eller skulle virke :)

Client laver en Job objekt og smider det ind i en workpool.
Nogle Workers henter Jobs fra WorkPool.
Når en worker henter et job, kalder den en funktion på Job der udfører noget, og sætter isFinished til true.

Efter at Klienten har smidt jobbet i workpool, laver den en getResult() på jobbet.

Derfor wait()´er jobbet til isFinished er true.

Fra Job
    public synchronized Object getResult()
    {   
        while (!isFinished)
        {
            try { wait(); } catch (InterruptedException re) {
                //dont care, while loop will check if we are done
            }
        }
        return result;
    }


Fra Worker
        while (true)
        {
            //Look for work
            SQLJob work = workpool.getWork(); //will sleep here till theres work todo.
            //do work
            work.run(sql);
            System.out.println ("Worker "+id+" finished job "+job);
            job++;
        }


Fra workpool
    public synchronized void addWork(SQLJob job)
    {
        queue.enQueue(job);
        this.notifyAll();
    }
   
    public synchronized SQLJob getWork()
    {
        while (queue.isEmpty())
        {
            try
            {
                wait();
            } catch (InterruptedException re)
            {   
                //Work arrived, see it we gets it
            }
        }
        SQLJob job = (SQLJob)queue.deQueue();
        return job;
    }






Fejlen.
java.lang.IllegalMonitorStateException: current thread not owner
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:429)
        at Job.getResult(Job.java:22)
        at Client.handle(Client.java:279)
        at Client.run(Client.java:38)
        at java.lang.Thread.run(Thread.java:534)

Client og Worker er threads.



Jeg har overvejet at bruge join, men jeg skal jo ikke vente på en thread dør, men blot på et objekt ændre status.
Avatar billede arne_v Ekspert
11. januar 2004 - 10:28 #1
Hvor bliver isFinished sat ?
Avatar billede mora Nybegynder
11. januar 2004 - 14:21 #2
i slutningen af Job´s run
Avatar billede dsj Nybegynder
11. januar 2004 - 16:36 #3
Det virker temmelig underligt. IllegalMonitorStateException smides sædvanligvis som følge af manglende synkronisering ved brug af wait eller notify, og i dette tilfælde er metoden synkroniseret på this, som wait kaldes på. Det ser ud som om den brokker sig over, er at der mangler synkronisering ved kald af wait, men det gør der ikke...
Avatar billede dsj Nybegynder
11. januar 2004 - 16:42 #4
En løsning kunne måske være at kigge lidt på alternative og måske mere afprøvede design-mønstre; det virker umiddelbart underligt at jobbet har behov for en status der indikerer at jobbet er færdiggjort, men nu kender jeg heller ikke systemets dybere virkemåde.

Selv har jeg arbejdet meget med servere og førhen anvendt pool-mønstre (designmåden kan varieres). Efterhånden er jeg så småt gået væk fra at anvende worker-pools, da det ofte er ineffektivt, så længe resten af serveren er designet rigtigt, f.eks. event-baseret. Hvis du er interesseret, kan jeg give nogle forslag til designmåder, men så må jeg vide lidt mere om systemet, f.eks. I/O; anvender du java.nio.*?

Ud fra den viste kode er der ikke meget at komme efter, det ser ud som det skal synes jeg...
Avatar billede mora Nybegynder
11. januar 2004 - 17:18 #5
well.

grunden til workerthread er flere workers skal deles om arbejdet, de fleste opgaver er simple sql statements en worker fint kan klare alene, men engang imellem kommer der en tung en, og der kommer de flere workers ind, så de korte opgaver stadig udføres.

min klient laver et job, og kalder derefter getResult() getResult venter til isFinished er true, og sender så resultatet tilbage
Avatar billede dsj Nybegynder
11. januar 2004 - 18:09 #6
Det der giver mest mening er at have en worker pr. tilgængelig database-forbindelse, og udover det kun have nok tråde til at servicere klienterne, altså læse fra netværksforbindelserne, udføre arbejde forbundet med modtaget data (lige pånær database-arbejde), og skrive data tilbage til klienterne. Men newer mind, det er ikke dit problem.

Din klient laver et job; har du en eller to tråde pr. klient? I stedet for at at kalde getResult() efter klienten har lavet et job (og lade tråden vente og spilde sin tid), vil det være bedre at have en callback-metode på Job, som kan workeren kan kalde, når database-kaldet er udført. På denne måde sparer du også ventemekanismen, der giver problemet.
Avatar billede mora Nybegynder
11. januar 2004 - 18:18 #7
Hver worker har en forbindelse, men til samme database ?

prøver med callback lidt senere :)
Avatar billede arne_v Ekspert
11. januar 2004 - 18:36 #8
Hvis vi skal finde ud af hvad der er galt i den eksisterende kode, så
må vi se noget mere kode.

Der er ikke imiddelbart noget galt.
Avatar billede arne_v Ekspert
11. januar 2004 - 18:38 #9
Følgende eksempel ligner dit i logok (og det kører fint):

package worker;

public interface Job {
    public void execute();
}

package worker;

public class TestJob implements Job {
    private int v;
    private String lst[];
    public TestJob(int v, String[] lst) {
        this.v = v;
        this.lst = lst;
    }
    public void execute() {
        lst[v] = Thread.currentThread().getName();
    }
}

package worker;

import java.util.*;

public class Queue {
    private LinkedList q;
    public Queue() {
        q = new LinkedList();   
    }
    public synchronized void enqueue(Job j) {
        q.addLast(j);
        notifyAll();
    }
    public synchronized Job dequeue() throws InterruptedException {
        while(q.isEmpty()) {
            wait();
        }
        return (Job)q.removeFirst();
    }
    public synchronized boolean isDone() {
        return q.isEmpty();
    }
}

package worker;

public class Worker extends Thread {
    private Queue q;
    public Worker(Queue q) {
        this.q = q;
    }
    public void run() {
        boolean done = false;
        while(!done) {
            try {
                q.dequeue().execute();
            } catch (InterruptedException e) {
                done = true;
            }
        }
    }
}

package worker;

public class WorkerPool {
    private Worker[] wrk;
    public WorkerPool(int n, Queue q) {
        wrk = new Worker[n];
        for(int i = 0; i < wrk.length; i++) {
            wrk[i] = new Worker(q);
            wrk[i].start();
        }
    }
    public void killAll() {
        for(int i = 0; i < wrk.length; i++) {
            wrk[i].interrupt();
        }
    }
}

package worker;

import java.io.*;

public class Test {
    private final static int N = 10000;
    public static void main(String[] args) throws Exception {
        String[] t = new String[N];
        Queue q = new Queue();
        WorkerPool pool = new WorkerPool(100, q);
        for(int i = 0; i < N; i++) {
            q.enqueue(new TestJob(i, t));
        }
        while(!q.isDone()) {
            Thread.sleep(10);
        }
        pool.killAll();
        PrintWriter pw = new PrintWriter(new FileOutputStream("C:\\t.lis"));
        for(int i = 0; i < t.length; i++) {
            pw.println(t[i]);
        }
        pw.close();
    }
}
Avatar billede mora Nybegynder
11. januar 2004 - 20:19 #10
Har ladt Job indeholde mit id og et client objekt.

så worker kalder job.getowner().jobdone(job) når et job er færdigt
det virker, selvom job nok har lidt for mange info :)
Avatar billede dsj Nybegynder
11. januar 2004 - 21:35 #11
Det er meget normalt at et objekt Job indeholder nogle info, sådan er det. Derimod er det meget mere værd, hvis man kan undgå at tråde venter på at nogle betingelser bliver opfyldt, men i stedet kan lave noget andet i mellemtiden. Husk at det klient-objekt Job-instancerne får med ikke er et objekt, men en  reference til et objekt. Måske har du forøget en Job-instans' størrelse med 4-8 bytes, og det er virkelig ikke alverden.
Avatar billede dsj Nybegynder
11. januar 2004 - 21:45 #12
Job repræsenterer jo en event i din server, som ændrer tilstand undervejs, hvilket betyder at nogle data må ændre værdier. Desuden er det pænt design, at Job indeholder alle de oplysninger der skal til for at jobbet kan fuldføres.

Og ja, det kan ofte være effektivt at mere end én forbindelse til samme database. Når du udfører et SQL-kald fra din Java-server til en database (f.eks. en query), hentes alle data i det returnerede ResultSet ikke med det samme, men løbende, efterhånden som ResultSet.next() kaldes. Det gør at flere forbindelser til samme database med fordel kan udnyttes. Husk at forbindelsen til databaser i Java er realiseret med en Socket, der somme tider vil være blokkeret i en eller anden I/O operation, imens har serveren mulighed for at kommunikere med databasen gennem andre forbindelser. Derved opnås bedre performance i forbindelse med database-kommunikationen.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester