JavaRush /Java Blog /Random-TL /Concurrency sa Java. Tutorial - Mga konstruksyon na ligta...
0xFF
Antas
Донецк

Concurrency sa Java. Tutorial - Mga konstruksyon na ligtas sa thread.

Nai-publish sa grupo
Pagkatapos tingnan ang mga pangunahing panganib ng pagpapatakbo ng mga parallel na programa (gaya ng atomicity o visibility ), titingnan natin ang ilang mga disenyo ng klase na makakatulong sa atin na maiwasan ang mga nabanggit na pitfalls. Lumilikha ang ilan sa mga construct na ito ng mga bagay na ligtas sa thread, na nagbibigay-daan sa aming ligtas na ibahagi ang mga ito sa pagitan ng mga thread. Bilang halimbawa, titingnan natin ang mga bagay na hindi nababago at walang estado. Pipigilan ng ibang mga view ang iba't ibang thread na baguhin ang data, gaya ng mga lokal na variable ng thread. Maaari mong tingnan ang lahat ng source code sa Github . 1. Immutable Object Ang mga hindi nababagong bagay ay may estado (may data na kumakatawan sa estado ng object), ngunit ito ay nakatakda sa oras ng paglikha sa constructor, kapag ang isang instance ng object ay nalikha at ang estado ay hindi na mababago. Bagama't maaaring magpalit-palit ang mga thread, mayroon pa ring isang posibleng estado ang isang bagay. Dahil ang lahat ng mga field ay read-only, walang thread ang makakapagpabago sa data ng object. Dahil dito, ang isang hindi nababagong stream ay likas na ligtas sa thread. Ang klase ng Produkto ay nagpapakita ng hindi nababagong klase. Pinupunan nito ang lahat ng field nito sa constructor at wala sa mga iyon ang nagbabago: public final class Product { private final String id; private final String name; private final double price; public Product(String id, String name, double price) { this.id = id; this.name = name; this.price = price; } public String getId() { return this.id; } public String getName() { return this.name; } public double getPrice() { return this.price; } public String toString() { return new StringBuilder(this.id).append("-").append(this.name) .append(" (").append(this.price).append(")").toString(); } public boolean equals(Object x) { if (this == x) return true; if (x == null) return false; if (this.getClass() != x.getClass()) return false; Product that = (Product) x; if (!this.id.equals(that.id)) return false; if (!this.name.equals(that.name)) return false; if (this.price != that.price) return false; return true; } public int hashCode() { int hash = 17; hash = 31 * hash + this.getId().hashCode(); hash = 31 * hash + this.getName().hashCode(); hash = 31 * hash + ((Double) this.getPrice()).hashCode(); return hash; } } Sa ilang mga kaso, hindi magiging sapat ang paggawa ng finals sa mga field. Halimbawa, ang klase ng MutableProduct ay hindi nababago, bagama't ang lahat ng mga field ay pinal: Bakit ang klase sa itaas ay hindi nababago? Ang dahilan ay pinapayagan namin ang isang reference na makuha mula sa isang klase. Ang field na ' categories ' ay isang nababagong sanggunian, kaya kapag natanggap, maaaring baguhin ito ng kliyente. Upang ilarawan ito, isaalang-alang ang sumusunod na programa: At ang console output: Dahil ang field na 'mga kategorya' ay nababago at nakuha mula sa isang bagay, binago ng kliyente ang listahang ito. Ang isang bagay na dapat ay hindi nababago ay nabago, na nagreresulta sa isang bagong estado. Kung gusto mong kumatawan sa mga nilalaman ng isang listahan, maaari kang gumamit ng isang hindi mababago na representasyon ng listahan: 2. Stateless Object Ang mga stateless na bagay ay katulad ng hindi nababago na mga bagay, ngunit sa kasong ito ay wala silang estado, kahit isa. Kapag ang isang bagay ay isang stateless object, hindi nito kailangang ipagpatuloy ang anumang data sa pagitan ng mga tawag. Dahil walang estado na umiiral, walang thread ang makakaapekto sa resulta ng isa pang thread sa pamamagitan ng pagtawag sa mga pamamaraan ng object. Para sa kadahilanang ito, ang mga bagay na walang estado ay likas na ligtas sa thread. Ang klase ng ProductHandler ay isang halimbawa ng ganitong uri ng bagay. Naglalaman ito ng maraming operasyon sa mga bagay ng Produkto, at hindi ito nag-iimbak ng anumang data sa pagitan ng mga tawag. Ang resulta ng operasyon ay independiyente sa mga nakaraang tawag o anumang nakaimbak na data: Sa paraan ng sumCart, kino-convert ng ProductHandler ang listahan ng Produkto sa isang array para magamit sa isang para sa bawat loop upang umulit sa lahat ng mga elemento. Ang listahan ng iterator ay hindi ligtas sa thread at maaaring magtapon ng ConcurrentModificationException kung may mga pagbabago sa panahon ng pag-ulit. Depende sa iyong mga pangangailangan, maaari kang pumili ng ibang diskarte . 3. Mga Lokal na Variable ng Thread Ang mga lokal na variable ng thread ay ang mga variable na tinukoy sa loob ng isang thread. Walang ibang mga thread ang nakakakita sa kanila at hindi ito babaguhin. Ang unang uri ay mga lokal na variable. Sa halimbawa sa ibaba, ang kabuuang variable ay naka-imbak sa stack ng thread: public final class MutableProduct { private final String id; private final String name; private final double price; private final List categories = new ArrayList<>(); public MutableProduct(String id, String name, double price) { this.id = id; this.name = name; this.price = price; this.categories.add("A"); this.categories.add("B"); this.categories.add("C"); } public String getId() { return this.id; } public String getName() { return this.name; } public double getPrice() { return this.price; } public List getCategories() { return this.categories; } public List getCategoriesUnmodifiable() { return Collections.unmodifiableList(categories); } public String toString() { return new StringBuilder(this.id).append("-").append(this.name) .append(" (").append(this.price).append(")").toString(); } } public static void main(String[] args) { MutableProduct p = new MutableProduct("1", "a product", 43.00); System.out.println("Product categories"); for (String c : p.getCategories()) System.out.println(c); p.getCategories().remove(0); System.out.println("\nModified Product categories"); for (String c : p.getCategories()) System.out.println(c); } Product categories A B C Modified Product categories B C public List getCategoriesUnmodifiable() { return Collections.unmodifiableList(categories); } public class ProductHandler { private static final int DISCOUNT = 90; public Product applyDiscount(Product p) { double finalPrice = p.getPrice() * DISCOUNT / 100; return new Product(p.getId(), p.getName(), finalPrice); } public double sumCart(List cart) { double total = 0.0; for (Product p : cart.toArray(new Product[0])) total += p.getPrice(); return total; } } public double sumCart(List cart) { double total = 0.0; for (Product p : cart.toArray(new Product[0])) total += p.getPrice(); return total; } Tandaan lamang na kung tutukuyin mo ang isang sanggunian sa halip na isang primitive na variable at ibabalik ito, aalis ito sa mga hangganan nito. Maaaring hindi mo alam kung saan ginawa ang ibinalik na link. Ang code na tumatawag sa paraan ng sumCart ay maaaring mag-imbak nito sa isang static na field at payagan itong ma-access ng iba't ibang mga thread. Ang pangalawang uri ay ang ThreadLocal class . Nagbibigay ang klase na ito ng independiyenteng imbakan para sa bawat thread. Ang mga value na nakaimbak sa ThreadLocal ay available sa anumang code sa parehong thread. Ang klase ng ClientRequestId ay nagpapakita ng isang halimbawa ng paggamit ng klase ng ThreadLocale: Ang klase ng ProductHandlerThreadLocal ay gumagamit ng ClientRequestId upang ibalik ang parehong nabuong ID sa parehong thread: Kapag isinasagawa ang pangunahing pamamaraan, ang output ng console ay magpapakita ng iba't ibang mga ID para sa bawat thread. Halimbawa: Kung gagamit ka ng ThreadLocale, kailangan mong pangalagaan ang ilang mga panganib sa paggamit kapag pinagsasama ang mga thread (tulad ng sa mga application ng server). Maaari kang makakuha ng mga pagtagas ng memorya o pagtagas ng impormasyon sa pagitan ng mga kahilingan. Hindi na ako magdetalye tungkol dito dahil... Ang artikulong " Paano i-shoot ang iyong sarili sa paa gamit ang ThreadLocale " ay mahusay na nagpapakita kung paano ito maaaring mangyari. 4. Paggamit ng synchronization Ang isa pang paraan upang magbigay ng thread-safe na access sa mga bagay ay sa pamamagitan ng synchronization. Kung i-synchronize namin ang lahat ng pag-access sa isang reference, pagkatapos ay isang thread object lang ang makaka-access dito sa isang partikular na oras. Tatalakayin natin ito sa mga susunod na post. 5. Konklusyon Tumingin kami sa ilang mga pamamaraan na nagbibigay-daan sa iyo upang bumuo ng mga simpleng bagay na maaaring ma-access ng maraming mga thread. Mas mahirap na pigilan ang mga multi-threaded na error kung ang isang bagay ay maaaring magkaroon ng maraming estado. Sa kabilang banda, kung ang isang bagay ay maaaring magkaroon lamang ng isang estado o wala, hindi namin kailangang mag-alala tungkol sa maraming mga thread na nag-a-access sa bagay nang sabay-sabay. Ang orihinal ay narito . public class ClientRequestId { private static final ThreadLocal id = new ThreadLocal () { @Override protected String initialValue() { return UUID.randomUUID().toString(); } }; public static String get() { return id.get(); } } public class ProductHandlerThreadLocal { //Same methods as in ProductHandler class public String generateOrderId() { return ClientRequestId.get(); } } T1 - 23dccaa2-8f34-43ec-bbfa-01cec5df3258 T2 - 936d0d9d-b507-46c0-a264-4b51ac3f527d T2 - 936d0d9d-b507-46c0-a264-4b51ac3f527d T3 - 126b8359-3bcc-46b9-859a-d305aff22c7e ...
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION