Programavimas

„Java“ patarimas: kada naudoti „ForkJoinPool“ ir „ExecutorService“

„Fork / Join“ biblioteka, pristatyta „Java 7“, praplečia esamą „Java“ lygiagretumo paketą ir palaiko aparatūros lygiagretumą, kuris yra pagrindinis daugiagyslių sistemų bruožas. Šiame „Java“ patarime Madalinas Ilie demonstruoja „Java 6“ pakeitimo poveikį našumui ExecutorService klasė su „Java 7“ „ForkJoinPool“ žiniatinklio tikrintuvo programoje.

Žiniatinklio tikrintuvai, dar vadinami interneto vorais, yra raktai į paieškos sistemų sėkmę. Šios programos nuolatos nuskaito internetą, surenka milijonus puslapių duomenų ir siunčia juos atgal į paieškos sistemų duomenų bazes. Tada duomenys indeksuojami ir apdorojami algoritmiškai, todėl paieškos rezultatai yra greitesni ir tikslesni. Nors žinomiausi žiniatinklio tikrintuvai yra naudojami paieškos optimizavimui, jie taip pat gali būti naudojami atliekant automatines užduotis, pvz., Nuorodų patvirtinimą arba konkrečių duomenų (pvz., El. Pašto adresų) paiešką ir grąžinimą tinklalapių rinkinyje.

Architektūriniu požiūriu dauguma žiniatinklio tikrintuvų yra didelio našumo kelių gijų programos, nors ir gana paprastos funkcijos ir reikalavimai. Todėl žiniatinklio tikrintuvo sukūrimas yra įdomus būdas praktikuoti, taip pat palyginti, daugialypius arba vienu metu susijusius programavimo metodus.

„Java“ patarimų grąža!

„Java Tips“ yra trumpi, kodais paremti straipsniai, kviečiantys „JavaWorld“ skaitytojus pasidalinti savo programavimo įgūdžiais ir atradimais. Praneškite mums, jei turite patarimą, kuriuo galėtumėte pasidalinti su „JavaWorld“ bendruomene. Taip pat peržiūrėkite „Java“ patarimų archyvą, kur rasite daugiau savo bendraamžių patarimų programuoti.

Šiame straipsnyje apžvelgsiu du būdus, kaip rašyti žiniatinklio tikrintuvą: vieną naudojant „Java 6 ExecutorService“, o kitą - „Java 7“ „ForkJoinPool“. Kad galėtumėte sekti pavyzdžiais, savo kūrimo aplinkoje turite (šio rašymo metu) įdiegti „Java 7“ atnaujinimą 2, taip pat trečiosios šalies biblioteką „HtmlParser“.

Du „Java“ lygiagretumo požiūriai

ExecutorService klasė yra java.util.sąlyga revoliucija, įdiegta „Java 5“ (ir, žinoma, „Java 6“ dalis), kuri supaprastino „Java“ platformos gijų valdymą. ExecutorService yra vykdytojas, pateikiantis metodus, kaip valdyti asinchroninių užduočių pažangos stebėjimą ir nutraukimą. Prieš įvedant java.util.sąlyga, „Java“ kūrėjai rėmėsi trečiųjų šalių bibliotekomis arba rašė savo klases, kad galėtų valdyti programų suderinamumą.

„Fork / Join“, pristatytas „Java 7“, nėra skirtas pakeisti esamas lygiagrečių paslaugų klases ar konkuruoti su jomis; vietoj to jas atnaujina ir užbaigia. „Fork / Join“ sprendžia skaldymo ir užkariavimo poreikį arba rekursinis užduočių apdorojimas „Java“ programose (žr. Ištekliai).

„Fork / Join“ logika yra labai paprasta: (1) kiekvieną didelę užduotį atskirti (šakute) į mažesnes; (2) kiekvieną užduotį apdorokite atskira gija (jei reikia, jas išskirstykite į dar mažesnes užduotis); (3) prisijungti prie rezultatų.

Dvi sekančios žiniatinklio tikrinimo programos yra paprastos programos, parodančios „Java 6“ ypatybes ir funkcionalumą ExecutorService ir „Java 7“ „ForkJoinPool“.

Interneto tikrintuvo kūrimas ir palyginimas

Mūsų interneto tikrintuvo užduotis bus rasti nuorodas ir jomis vadovautis. Jo tikslas gali būti nuorodų patvirtinimas arba duomenų rinkimas. (Pavyzdžiui, galite nurodyti programai internete ieškoti Angelinos Jolie arba Brado Pitto nuotraukų.)

Programos architektūrą sudaro šie elementai:

  1. Sąsaja, atskleidžianti pagrindines operacijas sąveikaujant su nuorodomis; y., gaukite aplankytų nuorodų skaičių, įtraukite į eilę naujų nuorodų, kurias norite aplankyti, pažymėkite nuorodą kaip aplankytą
  2. Šios sąsajos diegimas, kuris taip pat bus programos pradinis taškas
  3. Gijos / rekursinis veiksmas, kuris palaikys verslo logiką, kad patikrintų, ar nuoroda jau aplankyta. Jei ne, jis surinks visas nuorodas atitinkamame puslapyje, sukurs naują giją / rekursinę užduotį ir pateiks ją ExecutorService arba „ForkJoinPool“
  4. An ExecutorService arba „ForkJoinPool“ tvarkyti laukimo užduotis

Atminkite, kad nuoroda laikoma „aplankyta“, kai visos nuorodos atitinkamame puslapyje yra grąžintos.

Be paprasto kūrimo palyginimo naudojant „Java 6“ ir „Java 7“ turimus lygiagretumo įrankius, mes palyginsime programų našumą remdamiesi dviem gairėmis:

  • Paieškos aprėptis: Matuoja laiką, kurio reikia norint apsilankyti 1 500 ryškus nuorodos
  • Apdorojimo galia: Matuoja laiką, reikalingą apsilankyti 3000, sekundėmis neišsiskiriantis nuorodos; tai yra kaip matuoti, kiek kilobitų per sekundę apdoroja jūsų interneto ryšys.

Nors šie santykiniai kriterijai yra gana paprasti, tam tikriems programos reikalavimams teks bent mažas langas į „Java 6“ ir „Java 7“ lygiagretumo našumą.

„Java 6“ žiniatinklio tikrintuvas, sukurtas naudojant „ExecutorService“

„Java 6“ žiniatinklio tikrintuvui įgyvendinti naudosime fiksuoto gijos 64 gijų grupę, kurią sukursime paskambinę Executors.newFixedThreadPool (int) gamyklos metodas. 1 sąrašas rodo pagrindinį klasės įgyvendinimą.

Sąrašas 1. „WebCrawler“ sukūrimas

paketo insidecoding.webcrawler; importuoti java.util.Collection; importuoti java.util.Collections; importuoti java.util.concurrent.ExecutorService; importuoti java.util.concurrent.Vykdytojai; importuoti insidecoding.webcrawler.net.LinkFinder; importuoti java.util.HashSet; / ** * * @author Madalin Ilie * / viešoji klasė „WebCrawler6“ įgyvendina „LinkHandler“ {privatų galutinį rinkinį aplankytaLinks = Collections.synchronizedSet (naujas HashSet ()); // privati ​​galutinė kolekcija aplankytaLinks = Collections.synchronizedList (naujas ArrayList ()); asmeninės eilutės URL; privatus ExecutorService execService; public WebCrawler6 (eilutė pradedantURL, int maxThreads) {this.url = pradedantisURL; execService = Executors.newFixedThreadPool (maxThreads); } @Paisyti viešą tuštumą „queueLink“ (String link) išmeta išimtį {startNewThread (link); } @Paisyti viešojo int dydį () {grįžti aplankytasLinks.size (); } @Paisyti viešą nieką addVisited (String s) {visitLinks.add (s); } @Paisyti viešą loginę aplankytą aplanką (eilutės) {grįžti aplankytos nuorodos. Yra (-os); } private void startNewThread (String link) išmeta išimtį {execService.execute (new LinkFinder (link, this)); } private void startCrawling () meta išimtį {startNewThread (this.url); } / ** * @param argumentuoja komandinės eilutės argumentus * / public static void main (String [] args) išmeta išimtį {new WebCrawler ("// www.javaworld.com", 64) .startCrawling (); }}

Pirmiau „WebCrawler“ konstruktoriaus, mes sukuriame fiksuoto dydžio gijų rinkinį iš 64 gijų. Tada mes pradedame programą paskambinę startCrawling metodas, kuris sukuria pirmąją giją ir pateikia ją ExecutorService.

Tada sukursime „LinkHandler“ sąsaja, kuri atskleidžia pagalbinių metodų sąveiką su URL. Reikalavimai yra šie: (1) pažymėkite URL kaip aplankytą naudodami addVisited () metodas; (2) gauti aplankytų URL skaičių per dydis () metodas; (3) nustatyti, ar URL jau buvo aplankytas, naudodamas aplankė () metodas; ir (4) pridėti naują URL į eilę per „queueLink“ () metodas.

Sąrašas 2. „LinkHandler“ sąsaja

paketo insidecoding.webcrawler; / ** * * @author Madalin Ilie * / viešoji sąsaja „LinkHandler“ {/ ** * Nuorodą patalpina į eilę * @param nuoroda * @throws Exception * / negaliojanti eilutėLink (String link) išmeta Exception; / ** * Grąžina aplankytų nuorodų skaičių * @return * / int size (); / ** * Tikrina, ar nuoroda jau buvo aplankyta * @param link * @return * / loginė aplankyta (String link); / ** * Pažymi šią nuorodą kaip aplankytą * @param nuoroda * / void addVisited (String link); }

Dabar, kai mes tikriname puslapius, turime paleisti likusias gijas, kurias mes darome per „LinkFinder“ sąsaja, kaip parodyta 3 sąraše. Atkreipkite dėmesį į linkHandler.queueLink (l) linija.

Sąrašas 3. „LinkFinder“

paketas insidecoding.webcrawler.net; importuoti java.net.URL; importuoti org.htmlparser.Parser; importuoti org.htmlparser.filters.NodeClassFilter; importuoti org.htmlparser.tags.LinkTag; importuoti org.htmlparser.util.NodeList; importuoti insidecoding.webcrawler.LinkHandler; / ** * * @author Madalin Ilie * / viešoji klasė „LinkFinder“ įgyvendina „Runnable {private String url; privatus „LinkHandler“ linkHandler; / ** * Naudota fot statistika * / privati ​​statinė galutinė ilga t0 = System.nanoTime (); public LinkFinder (String url, LinkHandler handler) {this.url = url; this.linkHandler = prižiūrėtojas; } @Paisyti viešą nieką run () {getSimpleLinks (url); } private void getSimpleLinks (String url) {// jei dar nesilankėte, jei (! linkHandler.visited (url)) {bandykite {URL uriLink = naujas URL (url); Analizatoriaus analizatorius = naujas analizatorius (uriLink.openConnection ()); „NodeList“ sąrašas = parser.extractAllNodesThatMatch (naujas „NodeClassFilter“ („LinkTag.class“)); Sąrašo URL = new ArrayList (); for (int i = 0; i <list.size (); i ++) {LinkTag ištrauktas = (LinkTag) sąrašas.elementAt (i); if (! extracted.getLink (). isEmpty () &&! linkHandler.visited (extracted.getLink ())) {urls.add (extract.getLink ()); }} // aplankėme šią URL nuorodąHandler.addVisited (url); if (linkHandler.size () == 1500) {System.out.println ("Laikas aplankyti 1500 skirtingų nuorodų =" + (System.nanoTime () - t0)); } for (String l: urls) {linkHandler.queueLink (l); }} gaudyti (e išimtis) {// dabar ignoruoti visas klaidas}}}}

Logikos logika „LinkFinder“ yra paprasta: (1) pradedame analizuoti URL; (2) surinkę visas nuorodas atitinkamame puslapyje, pažymime puslapį kaip aplankytą; ir (3) kiekvieną rastą nuorodą siunčiame į eilę paskambinę „queueLink“ () metodas. Šis metodas iš tikrųjų sukurs naują giją ir nusiųs ją į ExecutorService. Jei baseine yra „nemokamų“ gijų, gija bus vykdoma; kitaip jis bus pastatytas į laukiančiųjų eilę. Pasiekę 1500 skirtingų aplankytų nuorodų, atspausdiname statistiką ir programa toliau veikia.

„Java 7“ žiniatinklio tikrintuvas su „ForkJoinPool“

„Fork / Join“ sistema, įdiegta „Java 7“, iš tikrųjų yra „Divide and Conquer“ algoritmo įgyvendinimas (žr. „Ištekliai“), kuriame pagrindinis „ForkJoinPool“ vykdo šakojimąsi „ForkJoinTask“s. Šiame pavyzdyje naudosime a „ForkJoinPool“ „paremtas“ 64 gijomis. aš sakau paremtas nes „ForkJoinTask“s yra lengvesni nei siūlai. Programoje „Fork / Join“ daugybę užduočių gali priglobti mažesnis gijų skaičius.

Panašiai kaip „Java 6“ diegimas, mes pradedame iš karto „WebCrawler“ konstruktorius a „ForkJoinPool“ objektas, paremtas 64 gijomis.

Sąrašas 4. „Java 7 LinkHandler“ diegimas

paketo insidecoding.webcrawler7; importuoti java.util.Collection; importuoti java.util.Collections; importuoti java.util.concurrent.ForkJoinPool; importuoti „insidecoding.webcrawler7.net.LinkFinderAction“; importuoti java.util.HashSet; / ** * * @author Madalin Ilie * / viešoji klasė „WebCrawler7“ įgyvendina „LinkHandler“ {privatų galutinį rinkinį aplankytaLinks = Collections.synchronizedSet (naujas „HashSet“ ()); // privati ​​galutinė kolekcija aplankytaLinks = Collections.synchronizedList (naujas ArrayList ()); asmeninės eilutės URL; privatus „ForkJoinPool“ pagrindinis baseinas; public WebCrawler7 (eilutė pradedantURL, int maxThreads) {this.url = pradedantisURL; mainPool = naujas „ForkJoinPool“ („maxThreads“); } private void startCrawling () {mainPool.invoke (new LinkFinderAction (this.url, this)); } @ Nepaisyti viešojo int dydžio () {grįžti aplankytasLinks.size (); } @Paisyti viešą nieką addVisited (String s) {visitLinks.add (s); } @Paisyti viešą loginę aplankytą aplanką (eilutės) {grįžti aplankytos nuorodos. Yra (-os); } / ** * @param argumentuoja komandinės eilutės argumentus * / public static void main (String [] args) išmeta išimtį {new WebCrawler7 ("// www.javaworld.com", 64) .startCrawling (); }}

Atkreipkite dėmesį, kad „LinkHandler“ sąsaja 4 sąraše yra beveik tokia pati kaip „Java 6“ diegimas iš „Listing 2“. Trūksta tik „queueLink“ () metodas. Svarbiausi metodai, į kuriuos reikia atkreipti dėmesį, yra konstruktorius ir startCrawling () metodas. Konstruktoriuje mes sukuriame naują „ForkJoinPool“ paremtas 64 gijomis. (Aš pasirinkau 64 gijas, o ne 50 ar kokį kitą apvalų skaičių, nes „ForkJoinPool“ Javadoc teigiama, kad gijų skaičius turi būti dviejų galia.) Baseinas kviečia naują „LinkFinderAction“, kuris rekursyviai remsis toliau „ForkJoinTasks“. 5 sąraše rodoma „LinkFinderAction“ klasė:

$config[zx-auto] not found$config[zx-overlay] not found