[JAVA] Thread problemen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Hallo,

een tijd terug heb ik in Java een programma geschreven wat afbeeldingen in een bepaalde map resized en in een nieuwe map op slaat. Dit werkt allemaal goed alleen vond ik het nogal langzaam dus besloot ik om het multithreaded te maken.

Deze refactoring is bijna klaar maar ikz it nog met een klein probleem. Ik lees eerst een directory en zet alle jpg files in deze directory in een vector. Deze vector deel ik vervolgens in een aantal sublists (het aantal sublists is afhankelijk van het aantal processoren). deze sublists worden ieder in een eigen thread gebruikt om afbeeldingen te kunnen resizen.

Als ik echter twee of meer threads ga starten krijg ik een raar fenomeen. De twee threads starten en de eerste paar files worden omgezet en opgeslagen en op een bepaald moment kapt de laatst gestarte thread ermee met als resultaat dat iets meer dan de helft (even vanuit gaande dat we het over 2 threads hebben) van de files zijn geresized en niet zoals verwacht allemaal.

Ik maak gebruik van de volgende code om subthreads aan te maken

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
        ExecutorService exec = (ExecutorService) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        
        Vector<File> files = this.sourceDirectory.list(new ImageFileFilter());
        
        if(files.size() > 0)
        {
            this.setChanged();
            this.notifyObservers(new ProgressMessage(String.valueOf(files.size()),ProgressMessage.STATUS_FILECOUNT));
            if(!this.destinationDirectory.exists())
            {
                this.destinationDirectory.mkdir();
                
            }           
            if(!this.thmbDestinationDirectory.exists())
            {
                this.thmbDestinationDirectory.mkdir();
            }
            
            int modulo = files.size() % maxSlices;
    
            int sliceSize = (files.size()-modulo)/maxSlices;
            
            lBound = modulo;
            uBound = sliceSize;
            
            for(int i = 0; i < maxSlices;i++)
            {
                System.out.println("lBound :"+lBound+" - "+uBound);
                exec.submit(new ResizeJob(new Vector(files.subList(lBound,uBound)),this.percentage,this.thumbPercentage,this.quality,this.destinationDirectory,i,this));
                lBound = uBound;
                uBound = uBound + sliceSize;
            }
            
        }else{
            this.setChanged();
            this.notifyObservers(MultiThreadedImageHandler.MSG_DONE);
        }


in elke ResizeJob wordt de volgende code uitgevoerd

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        BufferedImage thumb, dst, src;
        Graphics2D dstGraphics, thumbGraphics;
        int w,h,wThumb,hThumb;
        
        for(Iterator it = this.files.iterator(); it.hasNext();)
        {
            System.out.println(this.number + " processing file :"+i+" of "+this.files.size());
            File f = (File) it.next();
            System.out.println(f.toString());
            if(f.exists() && f.isFile())
            {
                System.out.println("doing");
                try {
                    src = ImageIO.read(f);
                    w = Integer.parseInt(String.valueOf((src.getWidth()/100)*scale));
                    
                    h = Integer.parseInt(String.valueOf((src.getHeight()/100)*scale));
                    
                    
                    dst = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
                    
                    dstGraphics = dst.createGraphics();
                                        
                    dstGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    dstGraphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                    dstGraphics.drawImage(src, 0, 0, w, h, null);
                    
                    File resFile = new File(this.destination.getAbsolutePath()+"/"+f.getName());
                    File thumbFile = new File(this.destination.getAbsolutePath()+"/thumbs/");
                    
                    wThumb = Integer.parseInt(String.valueOf((src.getWidth()/100)*scaleThumb));
                    hThumb = Integer.parseInt(String.valueOf((src.getHeight()/100)*scaleThumb));
                    thumb = new BufferedImage(wThumb,hThumb,BufferedImage.TYPE_INT_RGB);
                    thumbGraphics = thumb.createGraphics();
                    thumbGraphics.drawImage(dst,0,0,wThumb,hThumb,null);
                    
                    thumbFile = new File(this.destination.getAbsolutePath()+"/thumbs/"+f.getName());
                    System.out.println("write it");
                    
                    ImageIO.write(thumb, "JPG", thumbFile);
                    ImageIO.write(dst, "JPG", resFile);
                    
                    thumb.flush();
                    dst.flush();
                } catch (Exception e) {
                    System.out.println("Error"+e.toString());
                    System.out.println(e.getMessage());
                }
            }
            i++;
        }


Ik dacht eerst dat het een simpel geval van deadlocking was maar de twee threads delen helemaal geen resources dus volgens mij kan het dat niet zijn. Wie kan mij helpen met dit probleem(pje)

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • CoolGamer
  • Registratie: Mei 2005
  • Laatst online: 08:27

CoolGamer

What is it? Dragons?

Ik heb even de code lopen doorkijken naar wat het zou kunnen zijn, maar ik zie het zo snel niet. Maar ik heb wel een tip, loop de code eens door met een debugger en kijk bij elke regel of de waarde overeen komt met de verwachte waarde. Zet een breakpoint aan het begin van de threads zodat je kan controleren welke files al in de lijst staan.

Ik weet niet welke IDE je gebruikt, maar ik neem aan dat die wel in het bezit zijn van een geïntegreerde debugger.




Waarom begint de lBound bij "files.size() % maxSlices". Die moet toch gewoon bij 0 beginnen, maakt niet uit hoeveel Slices er zijn?

¸.·´¯`·.¸.·´¯`·.¸><(((º>¸.·´¯`·.¸><(((º>¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸<º)))><¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸


Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Ik heb zojuist even Java Visual VM aangezet en zag dat de heap size voor mijn progje ingesteld staat op 5mb standaard. Het probleem werd dan ook veroorzaakt door dat de threads bij het inlezen van een image file al 60mb gebruikte (hier moet ik nog naar kijken of ik dat efficienter kan krijgen in Java. Gebruik nu ImageIO.read() iemand anders een idee ? ) waardoor er een outofmemory exception wordt gegooid. Waarom ik die dan niet in mijn console heb gezien is mij nog een raadsel.

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • CoolGamer
  • Registratie: Mei 2005
  • Laatst online: 08:27

CoolGamer

What is it? Dragons?

Het is vaak aan te raden om een try..catch blok om thread-code heen te zetten, zodat een fout niet ongezien verloren gaat. Dan was het misschien hier ook eerder opgemerkt.

¸.·´¯`·.¸.·´¯`·.¸><(((º>¸.·´¯`·.¸><(((º>¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸<º)))><¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸


Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Zoals ik ook in mijn vorige post aangegeven heb weet ik nog steeds niet waarom de outofmemory exception neit is opgegooid aangezien er wel een try/catch om de code staat. Als ik gebruik maak van plain old threads ipv de threadpool functionaliteit van java 5.0 komt die error pas naar boven drijven. Gebruik ik de executorservice van java 5.0 dan komt die error niet naar boven.

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • bredend
  • Registratie: September 2001
  • Laatst online: 18-09 21:45
Misschien moet je in elke thread het resterende aantal files printen zodat je kan zien of de lijsten wel kloppen.

Weet je zeker dat er met dit stukje code
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int modulo = files.size() % maxSlices;
    
            int sliceSize = (files.size()-modulo)/maxSlices;
            
            lBound = modulo;
            uBound = sliceSize;
            
            for(int i = 0; i < maxSlices;i++)
            {
                System.out.println("lBound :"+lBound+" - "+uBound);
                exec.submit(new ResizeJob(new Vector(files.subList(lBound,uBound)),this.percentage,this.thumbPercentage,this.quality,this.destinationDirectory,i,this));
                lBound = uBound;
                uBound = uBound + sliceSize;
            }

geen files in meerdere lijsten staan?

Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
bredend schreef op zondag 26 juli 2009 @ 00:44:
Misschien moet je in elke thread het resterende aantal files printen zodat je kan zien of de lijsten wel kloppen.

Weet je zeker dat er met dit stukje code
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int modulo = files.size() % maxSlices;
    
            int sliceSize = (files.size()-modulo)/maxSlices;
            
            lBound = modulo;
            uBound = sliceSize;
            
            for(int i = 0; i < maxSlices;i++)
            {
                System.out.println("lBound :"+lBound+" - "+uBound);
                exec.submit(new ResizeJob(new Vector(files.subList(lBound,uBound)),this.percentage,this.thumbPercentage,this.quality,this.destinationDirectory,i,this));
                lBound = uBound;
                uBound = uBound + sliceSize;
            }

geen files in meerdere lijsten staan?
Ik ga er vanmiddag nog een keertje naar kijken het is nu al redelijk laat. Het progje werkt inmiddels omdat ik de heapsize van 5mb naar 250mb heb gezet en eens me de java visualvm heb bekeken wat er nu precies zoveel geheugen vreet.

offtopic:
Webgnome vind het nog steeds raar dat in het begin er helemaal geen outofmemory exception werd opgegooid...

[ Voor 5% gewijzigd door Webgnome op 26-07-2009 01:58 ]

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
Ik zie niet precies wat er mis gaat, maar ik zie wel een aantal punten voor verbetering:
Java:
1
2
3
4
5
w = Integer.parseInt(String.valueOf((src.getWidth()/100)*scale));
h = Integer.parseInt(String.valueOf((src.getHeight()/100)*scale)); 
.....
wThumb = Integer.parseInt(String.valueOf((src.getWidth()/100)*scaleThumb));
hThumb = Integer.parseInt(String.valueOf((src.getHeight()/100)*scaleThumb)); 

Waarom doe je dit? Je rekent met integers en vervolgens converteer je naar een String om vervolgens meer terug te converteren naar een int ....

Dit volstaat (en als scale een double is, dan moet je nog even terugcasten naar int)
Java:
1
2
3
4
5
w = (src.getWidth()/100)*scale;
h = (src.getHeight()/100)*scale; 
....
wThumb = (src.getWidth()/100)*scaleThumb;
hThumb = (src.getHeight()/100)*scaleThumb; 


Daarnaast kan je ook gewoon een foreach loop gebruiken, waarbij je:
Java:
1
2
3
4
        for(Iterator it = this.files.iterator(); it.hasNext();)
        {
            System.out.println(this.number + " processing file :"+i+" of "+this.files.size());
            File f = (File) it.next(); 


Kunt vervangen door:
Java:
1
for (File f : files) {

Daarnaast refereer je in je code naar i die volgens mij nergens opgehoogd wordt.

Verder doe je:
Java:
1
2
3
File thumbFile = new File(this.destination.getAbsolutePath()+"/thumbs/");
//andere code die niks met thumbFile doet
thumbFile = new File(this.destination.getAbsolutePath()+"/thumbs/"+f.getName()); 


Verwijder de eerste create van thumbFile, of verander het in:
Java:
1
2
3
//andere code die niks met thumbFile doet
File thumbFile = new File(this.destination.getAbsolutePath()+"/thumbs/");
thumbFile = new File(thumbFile, f.getName());


Verder wordt het meestal aangeraden om de volledige stacktrace te printen en niet slechts de boodschap.

Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Webgnome schreef op zaterdag 25 juli 2009 @ 21:33:
Ik heb zojuist even Java Visual VM aangezet en zag dat de heap size voor mijn progje ingesteld staat op 5mb standaard
5mb standaard? Als je geen heapsize meegeeft is de minimale heap-grootte 2MB en maximale grootte 64MB. Als je een 64bits VM gebruikt worden die waarden vziw nog wat groter. (PS check de -server flag, dat zal in jouw geval de boel aardig kunnen versnellen als je niet standaard al de server-vm krijgt)

Niettemin is de default voor veel toepassingen te klein en daar was je al achter gekomen :) En de -Xmx vlag geeft je dan wat meer ruimte.

Overigens is het wellicht iets netter schaalbaar te maken door alle afbeeldingen in een queue te gooien, dan hoef je de lijst niet vooraf op te delen en loop je minder risico dat een thread een hele berg grote afbeeldingen krijgen en een ander allemaal kleintjes. Als je dan een concurrent uitvoering neemt hoef je verder zelf niet zo veel op synchronization issues te letten.

Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Bedankt voor de tips. Ik ga er zo weer aan werken dus dan zal ik eens kijken naar het gebruik van een queue en verder ga ik natuurlijk nog het een en ander proberen te optimaliseren.

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 15-05 16:29

Macros

I'm watching...

Wat ACM al zei. Wat ik meestal doe is dat ik niet een array/lijst/collection met objecten die ik wil verwerken maak, maar een collection met taken die ik wil verwerken.

Dus dan maak ik bijvoorbeeld dit:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
         
Collection<Callable<String>> tasks = new ArrayList<Callable<String>>(files.size());
for(final File file : files){
   tasks.add(new Callable() {
       public String call(){
           ...convert(file)...
           return file.getName();
        }
    };
}
try{
executor.invokeAll(tasks);
}catch(...){
}

"Beauty is the ultimate defence against complexity." David Gelernter


Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Ik maak zelf ook al gebruik van de fixedthreadpool maar dan met daarin threads die een lijst afhandelen die ze meekrijgen. Dit ga ik ombouwen, aan de hand van de info van ACM, naar een lijst met threads die dezelfde queue mee krijgen. Het aantal threads bepaald dan natuurlijk de mate waarin deze lijst wordt uitgelezen/bewerkt.

Ik had in het begin zelfs een thread die eerst alle File objecten ging omzetten naar BufferedImages maar na een beetje testen met ImageIO.Read kwam ik er al vrij snel achter dat dit zeker memory problemen oplevert aangezien een enkele jpg file inladen al 64mb (of iig iets in die richting ) benodigd.

Strava | AP | IP | AW


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:20
Je kan overwegen om zo snel mogelijk de dispose() methode van de tijdelijke Grapics2D instanties aan te roepen, zie de Grapics#dispose() API docs.

Acties:
  • 0 Henk 'm!

  • kasper_vk
  • Registratie: Augustus 2002
  • Laatst online: 08-04 20:48
Overweeg eens om van elke te resizen file een aparte job te maken; niet enkele jobs die elk weer meerdere files moeten resizen. Dan ben je wellicht ook van je probleem af.

The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' but 'That's funny...'


Acties:
  • 0 Henk 'm!

  • Webgnome
  • Registratie: Maart 2001
  • Laatst online: 08:21
Het probleem inmiddels al opgelost door gebruik te maken van de blockingqueue en de heapsize te vergroten. Het 'probleem' was het inladen van een Image via ImageIO.Read(). Deze vreet ontzettend veel geheugen en daar is niks aan te doen zover ik kan zien.

In mijn huidige implementatie heb ik er voor gekozen om een ArrayBlockingQueue te gebruiken die door een enkele producer thread word gevuld met data. Het maximum aan files dat tegelijk in deze que staat is het aantal processoren * 2. Ik heb dan ook net zoveel consumer threads als dat er processoren zijn.

Dit lijkt goed te werken en heb tot op heden nog geen problemen met geheugen gebruik gehad. Hiermee is mijn weekend taak voor nu opgelost. Deze was nameljik om in het progje wat ik heb gemaakt om files te resizen multithreading in te bakken om het geheel sneller te maken.
kasper_vk schreef op zondag 26 juli 2009 @ 14:37:
Overweeg eens om van elke te resizen file een aparte job te maken; niet enkele jobs die elk weer meerdere files moeten resizen. Dan ben je wellicht ook van je probleem af.
Dat doe ik al. Ik heb in de try/catch een finally block waar de objecten die ik niet meer nodig heb worden geflushed , disposed en op null worden gezet. Nogmaals. Het zijn niet de graphics2 objecten die veel geheugen gebruiken maar het is vooral de BufferedImage. Probeer maar eens een image van 2mb via ImageIO.Read() in te laden in java en je zult zien dat het geheugen gebruik omhoog schiet!


Edit 4:13pm

ik zat me net te bedenken dat ik het aantal producer threads moet vergroten om het echt sneller te maken aangezien het resizen en wegschrijven zelf veel sneller gaat dan het uitlezen van de file.

[ Voor 7% gewijzigd door Webgnome op 26-07-2009 16:14 ]

Strava | AP | IP | AW

Pagina: 1