JavaRush /Java Blog /Random-TL /Multithreading sa Java: kakanyahan, mga pakinabang at kar...

Multithreading sa Java: kakanyahan, mga pakinabang at karaniwang mga pitfalls

Nai-publish sa grupo
Kamusta! Una sa lahat, binabati kita: naabot mo na ang paksa ng Multithreading sa Java! Ito ay isang seryosong tagumpay, may isang mahabang paraan upang pumunta. Ngunit maghanda: isa ito sa pinakamahirap na paksa sa kurso. At ang punto ay hindi ang mga kumplikadong klase o maraming mga pamamaraan ang ginagamit dito: sa kabaligtaran, wala pang dalawang dosena. Ito ay higit na kailangan mong baguhin nang kaunti ang iyong pag-iisip. Noong nakaraan, ang iyong mga programa ay pinaandar nang sunud-sunod. Ang ilang mga linya ng code ay sumunod sa iba, ang ilang mga pamamaraan ay sumunod sa iba, at sa pangkalahatan ang lahat ay malinaw. Una, kalkulahin ang isang bagay, pagkatapos ay ipakita ang resulta sa console, pagkatapos ay wakasan ang programa. Upang maunawaan ang multithreading, pinakamahusay na mag-isip sa mga tuntunin ng concurrency. Magsimula tayo sa isang bagay na napakasimple :) Multithreading sa Java: kakanyahan, mga pakinabang at karaniwang mga pitfalls - 1Isipin na ang iyong pamilya ay lumilipat mula sa isang bahay patungo sa isa pa. Ang isang mahalagang bahagi ng paglipat ay ang pag-iimpake ng iyong mga libro. Marami kang naipon na libro, at kailangan mong ilagay ang mga ito sa mga kahon. Ngayon ikaw lang ang libre. Si nanay ay naghahanda ng pagkain, si kuya ay nangongolekta ng mga damit, at si ate ay pumunta sa tindahan. Magagawa mong mag-isa, at least, at, sa malao't madali, ikaw mismo ang makumpleto ang gawain, ngunit aabutin ito ng maraming oras. Gayunpaman, sa loob ng 20 minuto ay babalik ang iyong kapatid na babae mula sa tindahan, at wala siyang ibang gagawin. Para makasama ka niya. Ang gawain ay nanatiling pareho: ilagay ang mga libro sa mga kahon. Dalawang beses lang itong tumakbo nang mas mabilis. Bakit? Dahil ang gawain ay ginagawa nang magkatulad. Dalawang magkaibang "mga thread" (ikaw at ang iyong kapatid na babae) ay sabay na gumaganap ng parehong gawain at, kung walang magbabago, ang pagkakaiba sa oras ay magiging napakalaki kumpara sa isang sitwasyon kung saan gagawin mo ang lahat nang mag-isa. Kung makumpleto ng iyong kapatid ang kanyang gawain sa lalong madaling panahon, matutulungan ka niya, at magiging mas mabilis ang mga bagay.

Mga problemang nalulutas ng multithreading sa Java

Mahalaga, ang Java multithreading ay naimbento upang malutas ang dalawang pangunahing problema:
  1. Magsagawa ng maraming pagkilos nang sabay-sabay.

    Sa halimbawa sa itaas, ang iba't ibang mga thread (i.e. mga miyembro ng pamilya) ay nagsagawa ng ilang mga aksyon nang magkatulad: naghugas ng mga pinggan, pumunta sa tindahan, nagtupi ng mga bagay.

    Ang isang mas "programmer" na halimbawa ay maaaring ibigay. Isipin na mayroon kang program na may user interface. Kapag na-click ang button na Magpatuloy, dapat mangyari ang ilang kalkulasyon sa loob ng programa, at dapat makita ng user ang sumusunod na screen ng interface. Kung ang mga pagkilos na ito ay isinasagawa nang sunud-sunod, pagkatapos ng pag-click sa pindutang "Magpatuloy", ang programa ay mag-freeze lamang. Makikita ng user ang parehong screen na may button na "Magpatuloy" hanggang sa makumpleto ang lahat ng panloob na kalkulasyon at maabot ng programa ang bahagi kung saan magsisimulang iguhit ang interface.

    Well, maghintay tayo ng ilang minuto!

    Multithreading sa Java: kakanyahan, mga pakinabang at karaniwang mga pitfalls - 3

    Maaari rin naming gawing muli ang aming programa, o, gaya ng sinasabi ng mga programmer, "magparallelize." Hayaang maisagawa ang mga kinakailangang kalkulasyon sa isang thread, at pag-render ng interface sa isa pa. Karamihan sa mga computer ay may sapat na mapagkukunan para dito. Sa kasong ito, ang programa ay hindi magiging "hangal", at ang gumagamit ay mahinahon na lumipat sa pagitan ng mga screen ng interface nang hindi nababahala tungkol sa kung ano ang nangyayari sa loob. Hindi ito nakikialam :)

  2. Pabilisin ang mga kalkulasyon.

    Ang lahat ay mas simple dito. Kung ang aming processor ay may ilang mga core, at karamihan sa mga processor ay multi-core na ngayon, ang aming listahan ng mga gawain ay maaaring malutas nang magkatulad sa pamamagitan ng ilang mga core. Malinaw, kung kailangan nating lutasin ang 1000 mga problema at ang bawat isa sa kanila ay malulutas sa isang segundo, isang core ang makakayanan ang listahan sa loob ng 1000 segundo, dalawang core sa loob ng 500 segundo, tatlo sa loob lamang ng 333 segundo, at iba pa.

Ngunit, tulad ng nabasa mo na sa lektura, ang mga modernong sistema ay napakatalino, at kahit na sa isang computing core ay nagagawa nilang ipatupad ang parallelism, o pseudo-parallelism, kapag ang mga gawain ay ginagawa nang halili. Lumipat tayo mula sa mga pangkalahatang bagay patungo sa mga tiyak at kilalanin ang pangunahing klase sa library ng Java na may kaugnayan sa multithreading - java.lang.Thread. Sa mahigpit na pagsasalita, ang mga thread sa Java ay kinakatawan ng mga pagkakataon ng klase Thread. Iyon ay, upang lumikha at magpatakbo ng 10 mga thread, kakailanganin mo ng 10 mga bagay ng klase na ito. Isulat natin ang pinakasimpleng halimbawa:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
Upang lumikha at maglunsad ng mga thread, kailangan nating lumikha ng isang klase at magmana nito mula sa java.lang. Threadat i-override ang pamamaraan sa loob nito run(). Ang huli ay napakahalaga. Ito ay sa pamamaraan na run()inireseta namin ang lohika na dapat isagawa ng aming thread. Ngayon, kung gagawa kami ng isang instance MyFirstThreadat patakbuhin ito, ang pamamaraan run()ay magpi-print ng isang linya na may pangalan nito sa console: ang pamamaraan getName()ay nagpi-print ng pangalan ng "system" ng thread, na awtomatikong itinalaga. Bagaman, sa totoo lang, bakit "kung"? Gumawa tayo at subukan!
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Output ng console: Ako ay Thread! Ang pangalan ko ay Thread-2 Ako ay Thread! Ang pangalan ko ay Thread-1 Ako ay Thread! Ang pangalan ko ay Thread-0 Ako ay Thread! Ang pangalan ko ay Thread-3 Ako ay Thread! Ang pangalan ko ay Thread-6 Ako ay Thread! Ang pangalan ko ay Thread-7 Ako ay Thread! Ang pangalan ko ay Thread-4 Ako ay Thread! Ang pangalan ko ay Thread-5 Ako ay Thread! Ang pangalan ko ay Thread-9 Ako ay Thread! Ang pangalan ko ay Thread-8 Gumagawa kami ng 10 mga thread (mga bagay) MyFirstThreadna nagmana mula sa Threadat naglulunsad ng mga ito sa pamamagitan ng pagtawag sa pamamaraan ng object start(). Pagkatapos tumawag sa isang pamamaraan , start()ang pamamaraan nito ay magsisimulang gumana run(), at ang lohika na nakasulat dito ay naisakatuparan. Pakitandaan: ang mga pangalan ng thread ay hindi maayos. Ito ay medyo kakaiba, bakit hindi sila pinatay sa turn: Thread-0, Thread-1, Thread-2at iba pa? Ito ay eksaktong isang halimbawa kung kailan hindi gagana ang karaniwang, "sequential" na pag-iisip. Ang katotohanan ay sa kasong ito ay naglalabas lamang kami ng mga utos upang lumikha at maglunsad ng 10 mga thread. Sa anong pagkakasunud-sunod na dapat silang ilunsad ay napagpasyahan ng thread scheduler: isang espesyal na mekanismo sa loob ng operating system. Napakakomplikadong paksa kung gaano ito nakabalangkas at sa kung anong prinsipyo ito gumagawa ng mga desisyon, at hindi na natin ito sasabak ngayon. Ang pangunahing bagay na dapat tandaan ay hindi makokontrol ng programmer ang pagkakasunud-sunod ng pagpapatupad ng thread. Upang mapagtanto ang kabigatan ng sitwasyon, subukang patakbuhin ang pamamaraan main()mula sa halimbawa sa itaas nang ilang beses. Pangalawang console output: Ako ay Thread! Ang pangalan ko ay Thread-0 Ako ay Thread! Ang pangalan ko ay Thread-4 Ako ay Thread! Ang pangalan ko ay Thread-3 Ako ay Thread! Ang pangalan ko ay Thread-2 Ako ay Thread! Ang pangalan ko ay Thread-1 Ako ay Thread! Ang pangalan ko ay Thread-5 Ako ay Thread! Ang pangalan ko ay Thread-6 Ako ay Thread! Ang pangalan ko ay Thread-8 Ako ay Thread! Ang pangalan ko ay Thread-9 Ako ay Thread! Ang pangalan ko ay Thread-7 Third console output: Ako ay Thread! Ang pangalan ko ay Thread-0 Ako ay Thread! Ang pangalan ko ay Thread-3 Ako ay Thread! Ang pangalan ko ay Thread-1 Ako ay Thread! Ang pangalan ko ay Thread-2 Ako ay Thread! Ang pangalan ko ay Thread-6 Ako ay Thread! Ang pangalan ko ay Thread-4 Ako ay Thread! Ang pangalan ko ay Thread-9 Ako ay Thread! Ang pangalan ko ay Thread-5 Ako ay Thread! Ang pangalan ko ay Thread-7 Ako ay Thread! Ang pangalan ko ay Thread-8

Mga problemang nalilikha ng multithreading

Sa halimbawa sa mga aklat, nakita mo na ang multithreading ay nalulutas ang mga mahahalagang problema, at ang paggamit nito ay nagpapabilis sa gawain ng aming mga programa. Sa maraming kaso - maraming beses. Ngunit hindi para sa wala na ang multithreading ay itinuturing na isang kumplikadong paksa. Pagkatapos ng lahat, kung ginamit nang hindi tama, lumilikha ito ng mga problema sa halip na lutasin ang mga ito. Kapag sinabi kong "lumikha ng mga problema," hindi abstract ang ibig kong sabihin. Mayroong dalawang partikular na problema na maaaring idulot ng multithreading: deadlock at kundisyon ng lahi. Ang deadlock ay isang sitwasyon kung saan maraming mga thread ang naghihintay para sa mga mapagkukunan na inookupahan ng bawat isa, at wala sa mga ito ang maaaring magpatuloy sa pagpapatupad. Pag-uusapan natin ang higit pa tungkol dito sa mga lektura sa hinaharap, ngunit sa ngayon ay sapat na ang halimbawang ito: Multithreading sa Java: kakanyahan, mga pakinabang at karaniwang mga pitfalls - 4 Isipin na ang thread-1 ay gumagana sa ilang Object-1, at ang thread-2 ay gumagana sa Object-2. Ang programa ay nakasulat tulad nito:
  1. Hihinto sa pagtatrabaho ang Thread-1 sa Object-1 at lilipat sa Object-2 sa sandaling huminto ang Thread-2 sa Object 2 at lumipat sa Object-1.
  2. Hihinto sa pagtatrabaho ang Thread-2 sa Object-2 at lilipat sa Object-1 sa sandaling huminto ang Thread-1 sa Object 1 at lumipat sa Object-2.
Kahit na walang malalim na kaalaman sa multithreading, madali mong maunawaan na walang darating dito. Ang mga thread ay hindi kailanman magbabago ng lugar at maghihintay sa isa't isa magpakailanman. Ang pagkakamali ay tila halata, ngunit sa katotohanan ay hindi. Madali mo itong papayagan sa programa. Titingnan natin ang mga halimbawa ng code na nagdudulot ng deadlock sa mga sumusunod na lecture. Siyanga pala, ang Quora ay may mahusay na halimbawa sa totoong buhay na nagpapaliwanag kung ano ang deadlock . “Sa ilang estado sa India, hindi ka nila ibebenta ng lupang pang-agrikultura maliban kung nakarehistro ka bilang isang magsasaka. Gayunpaman, hindi ka rehistrado bilang isang magsasaka kung hindi ka nagmamay-ari ng lupang pang-agrikultura.” Grabe, ano masasabi ko! :) Ngayon tungkol sa kondisyon ng lahi - ang estado ng karera. Multithreading sa Java: kakanyahan, kalamangan at karaniwang mga pitfalls - 5Ang kundisyon ng lahi ay isang depekto sa disenyo sa isang multi-threaded system o application kung saan ang pagpapatakbo ng system o application ay nakasalalay sa pagkakasunud-sunod kung saan ang mga bahagi ng code ay isinasagawa. Tandaan ang halimbawa sa pagpapatakbo ng mga thread:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Ngayon isipin na ang programa ay responsable para sa pagpapatakbo ng isang robot na naghahanda ng pagkain! Inilalabas ng Thread-0 ang mga itlog sa refrigerator. Binubuksan ng Stream 1 ang kalan. Ang Stream-2 ay kumuha ng kawali at inilalagay ito sa kalan. Ang Stream 3 ay nagsisindi ng apoy sa kalan. Ang Stream 4 ay nagbubuhos ng mantika sa kawali. Binabasag ng Stream 5 ang mga itlog at ibinuhos sa kawali. Ang Stream 6 ay nagtatapon ng mga shell sa basurahan. Tinatanggal ng Stream-7 ang natapos na piniritong itlog mula sa init. Ang Potok-8 ay naglalagay ng piniritong itlog sa isang plato. Naghuhugas ng pinggan ang Stream-9. Tingnan ang mga resulta ng aming programa: Thread-0 executed Thread-2 thread executed Thread-1 thread executed Thread-4 thread executed Thread-9 thread executed Thread-5 thread executed Thread-8 thread executed Thread-7 thread executed Thread-7 thread executed -3 Thread-6 thread executed. Masaya ba ang script? :) At lahat dahil ang pagpapatakbo ng aming programa ay nakasalalay sa pagkakasunud-sunod kung saan ang mga thread ay naisakatuparan. Sa kaunting paglabag sa pagkakasunud-sunod, nagiging impiyerno ang aming kusina, at sinisira ng isang robot ang lahat sa paligid nito. Isa rin itong karaniwang problema sa multithreaded programming, na maririnig mo nang higit sa isang beses. Sa pagtatapos ng lektura, nais kong irekomenda sa iyo ang isang libro sa multithreading.
Multithreading sa Java: kakanyahan, kalamangan at karaniwang mga pitfalls - 6
Ang "Java Concurrency in Practice" ay isinulat noong 2006, ngunit hindi nawala ang kaugnayan nito. Sinasaklaw nito ang multithreaded programming sa Java, simula sa mga pangunahing kaalaman at nagtatapos sa isang listahan ng mga pinakakaraniwang error at antipattern. Kung sakaling magpasya kang maging isang multithreaded programming guru, ang aklat na ito ay dapat basahin. Magkita-kita tayo sa susunod na mga lecture! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION