हम कब उपयोग करते हैं AtomicReference
?
क्या सभी बहुआयामी कार्यक्रमों में वस्तुओं को बनाने की आवश्यकता है?
एक सरल उदाहरण प्रदान करें जहां AtomicReference का उपयोग किया जाना चाहिए।
हम कब उपयोग करते हैं AtomicReference
?
क्या सभी बहुआयामी कार्यक्रमों में वस्तुओं को बनाने की आवश्यकता है?
एक सरल उदाहरण प्रदान करें जहां AtomicReference का उपयोग किया जाना चाहिए।
जवाबों:
एक संदर्भ में परमाणु संदर्भ का उपयोग किया जाना चाहिए जहां आपको एक संदर्भ पर सरल परमाणु (यानी थ्रेड-सुरक्षित , गैर-तुच्छ) संचालन करने की आवश्यकता होती है, जिसके लिए मॉनिटर-आधारित सिंक्रनाइज़ेशन उचित नहीं है। मान लीजिए कि आप देखना चाहते हैं कि क्या कोई विशिष्ट फ़ील्ड केवल तभी वस्तु की स्थिति रहती है जब आप अंतिम बार जाँच करते हैं:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
परमाणु संदर्भ शब्दार्थ के कारण, आप इसका उपयोग तब भी कर सकते हैं, जब cache
ऑब्जेक्ट का उपयोग किए बिना थ्रेड्स के बीच साझा किया जाता है synchronized
। सामान्य तौर पर, जब तक आप नहीं जानते कि आप क्या कर रहे हैं, java.util.concurrent
बजाए नंगे के बजाय सिंक्रोनाइज़र या फ्रेमवर्क का उपयोग करना बेहतर है Atomic*
।
दो बेहतरीन डेड-ट्री संदर्भ जो आपको इस विषय से परिचित कराएंगे:
ध्यान दें कि (मुझे नहीं पता कि यह हमेशा सच रहा है) संदर्भ असाइनमेंट (यानी =
) स्वयं परमाणु है ( आदिम 64-बिट प्रकारों को अपडेट करना long
या जैसे double
परमाणु नहीं हो सकता है; लेकिन संदर्भ को अद्यतन करना हमेशा परमाणु है, भले ही यह 64 बिट हो) ) स्पष्ट रूप से उपयोग किए बिना Atomic*
। जावा भाषा विनिर्देश 3ed
देखें , धारा 17.7 ।
AtomicReference
चर को चिह्नित करना चाहिए volatile
क्योंकि जबकि रनटाइम गारंटी देता है कि संदर्भ असाइनमेंट परमाणु है, संकलक इस धारणा के तहत अनुकूलन कर सकता है कि चर अन्य थ्रेड द्वारा संशोधित नहीं किया जा रहा था।
AtomicReference
"; यदि आप इसका उपयोग कर रहे हैं , तो मेरी सलाह होगी कि आप विपरीत दिशा में जाएं और इसे चिह्नित करें final
ताकि संकलक अपने अनुसार अनुकूलन कर सके।
एक परमाणु संदर्भ का उपयोग करने के लिए आदर्श है जब आपको कई थ्रेड्स के बीच एक अपरिवर्तनीय वस्तु की स्थिति को साझा करने और बदलने की आवश्यकता होती है। यह एक सुपर घने बयान है इसलिए मैं इसे थोड़ा नीचे तोड़ दूंगा।
सबसे पहले, एक अपरिवर्तनीय वस्तु एक ऐसी वस्तु है जिसे निर्माण के बाद प्रभावी ढंग से नहीं बदला जाता है। अक्सर एक अपरिवर्तनीय वस्तु के तरीके उसी वर्ग के नए उदाहरण लौटाते हैं। कुछ उदाहरणों में लॉन्ग और डबल के रैपर वर्ग, साथ ही स्ट्रिंग, केवल कुछ का नाम शामिल हैं। ( JVM अपरिवर्तनीय वस्तुओं पर प्रोग्रामिंग कंजरेन्सी के अनुसार आधुनिक संगामिति का एक महत्वपूर्ण हिस्सा हैं)।
अगला, क्यों AtomicReference कि साझा मूल्य साझा करने के लिए एक अस्थिर वस्तु से बेहतर है। एक सरल कोड उदाहरण अंतर दिखाएगा।
volatile String sharedValue;
static final Object lock=new Object();
void modifyString(){
synchronized(lock){
sharedValue=sharedValue+"something to add";
}
}
हर बार जब आप उस अस्थिर क्षेत्र द्वारा संदर्भित स्ट्रिंग को उसके वर्तमान मूल्य के आधार पर संशोधित करना चाहते हैं, तो आपको सबसे पहले उस ऑब्जेक्ट पर एक लॉक प्राप्त करना होगा। यह इस दौरान कुछ अन्य थ्रेड को आने से रोकता है और नए स्ट्रिंग के संयोजन के बीच में मूल्य को बदलने से रोकता है। फिर जब आपका धागा फिर से शुरू होता है, तो आप दूसरे धागे का काम करते हैं। लेकिन ईमानदारी से यह कोड काम करेगा, यह साफ दिखता है, और यह ज्यादातर लोगों को खुश करेगा।
थोड़ी सी समस्या। यह धीमा है। खासकर अगर उस लॉक ऑब्जेक्ट का बहुत अधिक विवाद हो। Thats क्योंकि अधिकांश तालों के लिए OS सिस्टम कॉल की आवश्यकता होती है, और आपका धागा ब्लॉक हो जाएगा और अन्य प्रक्रियाओं के लिए रास्ता बनाने के लिए सीपीयू से संदर्भ बंद हो जाएगा।
अन्य विकल्प एक एटॉमिकरफ्रेंस का उपयोग करना है।
public static AtomicReference<String> shared = new AtomicReference<>();
String init="Inital Value";
shared.set(init);
//now we will modify that value
boolean success=false;
while(!success){
String prevValue=shared.get();
// do all the work you need to
String newValue=shared.get()+"lets add something";
// Compare and set
success=shared.compareAndSet(prevValue,newValue);
}
अब यह बेहतर क्यों है? ईमानदारी से यह कोड पहले की तुलना में थोड़ा कम साफ है। लेकिन वास्तव में कुछ महत्वपूर्ण है जो परमाणु परमाणु में हुड के नीचे होता है, और यह तुलना और स्वैप है। यह एक एकल सीपीयू निर्देश है, न कि ओएस कॉल, जो स्विच को बनाता है। यह CPU पर एक एकल निर्देश है। और क्योंकि ताले नहीं हैं, ऐसे मामले में कोई संदर्भ स्विच नहीं है जहां लॉक का उपयोग किया जाता है जो और भी अधिक समय बचाता है!
कैच एटॉमिक रिफरेंस के लिए है, इसमें असमान () कॉल का उपयोग नहीं किया जाता है, बल्कि अपेक्षित मूल्य के लिए == तुलना की जाती है। तो सुनिश्चित करें कि उम्मीद लूप में प्राप्त की गई वास्तविक वस्तु है।
worked
एक ही शब्दार्थ प्राप्त करने के लिए लूप करना होगा ।
यहाँ AtomicReference के लिए एक उपयोग मामला है:
इस श्रेणी पर विचार करें जो एक संख्या सीमा के रूप में कार्य करती है, और निम्न और ऊपरी संख्या सीमा को बनाए रखने के लिए अलग-अलग AtmomicInteger चर का उपयोग करती है।
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
सेट्लॉवर और सेटअप दोनों ही चेक-एक्ट-एक्ट सीक्वेंस हैं, लेकिन उन्हें परमाणु बनाने के लिए पर्याप्त लॉकिंग का उपयोग नहीं करते हैं। यदि संख्या सीमा रखती है (0, 10), और एक थ्रेड कॉल सेटओवर (5) जबकि एक और थ्रेड कॉल सेटपर (4), कुछ अशुभ समय के साथ दोनों बसने वालों में चेक पास करेगा और दोनों संशोधनों को लागू किया जाएगा। परिणाम यह है कि रेंज अब (5, 4) एक अमान्य स्थिति रखती है। इसलिए जबकि अंतर्निहित परमाणुइंटर थ्रेड सुरक्षित हैं, समग्र वर्ग नहीं है। यह ऊपरी और निचले सीमा के लिए अलग-अलग एटॉमिकइंटरेयर्स का उपयोग करने के बजाय एक एटॉमिकरेंस का उपयोग करके तय किया जा सकता है।
public class CasNumberRange {
// Immutable
private static class IntPair {
final int lower; // Invariant: lower <= upper
final int upper;
private IntPair(int lower, int upper) {
this.lower = lower;
this.upper = upper;
}
}
private final AtomicReference<IntPair> values =
new AtomicReference<IntPair>(new IntPair(0, 0));
public int getLower() {
return values.get().lower;
}
public void setLower(int lower) {
while (true) {
IntPair oldv = values.get();
if (lower > oldv.upper)
throw new IllegalArgumentException(
"Can't set lower to " + lower + " > upper");
IntPair newv = new IntPair(lower, oldv.upper);
if (values.compareAndSet(oldv, newv))
return;
}
}
public int getUpper() {
return values.get().upper;
}
public void setUpper(int upper) {
while (true) {
IntPair oldv = values.get();
if (upper < oldv.lower)
throw new IllegalArgumentException(
"Can't set upper to " + upper + " < lower");
IntPair newv = new IntPair(oldv.lower, upper);
if (values.compareAndSet(oldv, newv))
return;
}
}
}
आशावादी तालों को लगाते समय आप एटॉमिक रेफरेंस का उपयोग कर सकते हैं। आपके पास एक साझा ऑब्जेक्ट है और आप इसे 1 से अधिक थ्रेड से बदलना चाहते हैं।
जैसा कि अन्य सूत्र ने इसे संशोधित किया है और / इन 2 चरणों के बीच संशोधित कर सकते हैं। आपको इसे एक परमाणु ऑपरेशन में करने की आवश्यकता है। यह वह जगह है जहाँ AtomicReference मदद कर सकता है
यहाँ एक बहुत ही सरल उपयोग मामला है और इसका धागा सुरक्षा से कोई लेना-देना नहीं है।
लैम्ब्डा इनवोकेशन के बीच एक वस्तु साझा करने के लिए, AtomicReference
एक विकल्प है :
public void doSomethingUsingLambdas() {
AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();
soSomethingThatTakesALambda(() -> {
yourObjectRef.set(youObject);
});
soSomethingElseThatTakesALambda(() -> {
YourObject yourObject = yourObjectRef.get();
});
}
मैं यह नहीं कह रहा हूं कि यह अच्छा डिज़ाइन या कुछ भी है (यह सिर्फ एक तुच्छ उदाहरण है), लेकिन अगर आपके पास ऐसा मामला है जहां आपको लैम्बडा इनवोकेशन के बीच एक ऑब्जेक्ट साझा करने की आवश्यकता है, तो AtomicReference
एक विकल्प है।
वास्तव में आप किसी भी वस्तु का उपयोग कर सकते हैं जो एक संदर्भ रखती है, यहां तक कि एक संग्रह जिसमें केवल एक आइटम है। हालांकि, एटॉमिकरेंस एक सही फिट है।
मैं ज्यादा बात नहीं करूंगा। पहले से ही मेरे सम्मानित साथी मित्रों ने अपना बहुमूल्य इनपुट दिया है। इस ब्लॉग के अंतिम भाग में पूर्ण विकसित कोड किसी भी भ्रम को दूर करना चाहिए। यह मल्टी-थ्रेडेड परिदृश्य में छोटे कार्यक्रम की बुकिंग वाली मूवी सीट के बारे में है।
कुछ महत्वपूर्ण प्राथमिक तथ्य इस प्रकार हैं। 1> विभिन्न धागे केवल ढेर स्थान में उदाहरण और स्थिर सदस्य चर के लिए चुनाव लड़ सकते हैं। 2> वाष्पशील पढ़ना या लिखना पूरी तरह से परमाणु और क्रमबद्ध / पहले होता है और केवल स्मृति से किया जाता है। यह कहने से मेरा तात्पर्य यह है कि कोई भी पढ़ा हुआ पिछला लेख मेमोरी में फॉलो करेगा। और कोई भी लेख स्मृति से पिछले पढ़ने का पालन करेगा। तो एक अस्थिर के साथ काम करने वाले किसी भी धागे को हमेशा सबसे अद्यतित मूल्य दिखाई देगा। AtomicReference अस्थिरता की इस संपत्ति का उपयोग करता है।
निम्नलिखित परमाणु अणु के स्रोत कोड के कुछ हैं। परमाणु संदर्भ एक वस्तु संदर्भ को संदर्भित करता है। यह संदर्भ नीचे के रूप में AtomicReference उदाहरण में एक अस्थिर सदस्य चर है।
private volatile V value;
get () वेरिएबल का नवीनतम मान लौटाता है (जैसा कि वाष्पशील "तरीके से पहले होता है")।
public final V get()
निम्नलिखित परमाणु अनुसंधान का सबसे महत्वपूर्ण तरीका है।
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
तुलना एएंडसेट (उम्मीद, अपडेट) विधि जावा के असुरक्षित वर्ग की तुलना एएंडस्वापओबजेक्ट () विधि कहती है। असुरक्षित का यह तरीका कॉल देशी कॉल को आमंत्रित करता है, जो प्रोसेसर को एक निर्देश देता है। "उम्मीद" और "अद्यतन" प्रत्येक संदर्भ एक वस्तु।
यदि और केवल अगर AtomicReference उदाहरण के सदस्य चर "मान" को संदर्भित करता है उसी वस्तु को "उम्मीद" से संदर्भित किया जाता है, तो "अपडेट" को इस उदाहरण चर में सौंपा गया है, और "सही" वापस आ गया है। वरना झूठा लौटा दिया जाता है। पूरी बात परमाणु से की गई है। कोई दूसरा धागा बीच में नहीं रोक सकता। चूंकि यह एक एकल प्रोसेसर ऑपरेशन (आधुनिक कंप्यूटर वास्तुकला का जादू) है, यह अक्सर एक सिंक्रनाइज़ ब्लॉक का उपयोग करने की तुलना में तेज़ है। लेकिन याद रखें कि जब कई चर को परमाणु रूप से अद्यतन करने की आवश्यकता होती है, तो AtomicReference मदद नहीं करेगा।
मैं एक पूर्ण विकसित कोड जोड़ना चाहूंगा, जिसे ग्रहण में चलाया जा सकता है। यह कई भ्रम को साफ करेगा। यहां 22 उपयोगकर्ता (MyTh threads) 20 सीटें बुक करने की कोशिश कर रहे हैं। निम्नलिखित कोड स्निपेट है जिसके बाद पूर्ण कोड होता है।
कोड स्निपेट जहां 22 उपयोगकर्ता 20 सीटें बुक करने की कोशिश कर रहे हैं।
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
निम्नलिखित पूर्ण चलने वाला कोड है।
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class Solution {
static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
// list index
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
seats = new ArrayList<>();
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
for (Thread t : ths) {
t.join();
}
for (AtomicReference<Integer> seat : seats) {
System.out.print(" " + seat.get());
}
}
/**
* id is the id of the user
*
* @author sankbane
*
*/
static class MyTh extends Thread {// each thread is a user
static AtomicInteger full = new AtomicInteger(0);
List<AtomicReference<Integer>> l;//seats
int id;//id of the users
int seats;
public MyTh(List<AtomicReference<Integer>> list, int userId) {
l = list;
this.id = userId;
seats = list.size();
}
@Override
public void run() {
boolean reserved = false;
try {
while (!reserved && full.get() < seats) {
Thread.sleep(50);
int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
// seats
//
AtomicReference<Integer> el = l.get(r);
reserved = el.compareAndSet(null, id);// null means no user
// has reserved this
// seat
if (reserved)
full.getAndIncrement();
}
if (!reserved && full.get() == seats)
System.out.println("user " + id + " did not get a seat");
} catch (InterruptedException ie) {
// log it
}
}
}
}
हम AtomicReference का उपयोग कब करते हैं?
AtomicReference तुल्यकालिककरण के उपयोग के बिना atomically चर मूल्य को अद्यतन करने के लिए लचीला तरीका है।
AtomicReference
एकल चर पर लॉक-थ्रेड-थ्रेड सुरक्षित प्रोग्रामिंग का समर्थन करें।
उच्च स्तर के समवर्ती एपीआई के साथ थ्रेड सुरक्षा प्राप्त करने के कई तरीके हैं । परमाणु चर कई विकल्पों में से एक है।
Lock
ऑब्जेक्ट लॉकिंग मुहावरों का समर्थन करते हैं जो कई समवर्ती अनुप्रयोगों को सरल बनाते हैं।
Executors
थ्रेड को लॉन्च करने और प्रबंधित करने के लिए एक उच्च-स्तरीय एपीआई को परिभाषित करें। Java.util.concurrent द्वारा प्रदान किए गए निष्पादन कार्यान्वयन बड़े पैमाने के अनुप्रयोगों के लिए उपयुक्त थ्रेड पूल प्रबंधन प्रदान करते हैं।
समवर्ती संग्रह डेटा के बड़े संग्रह को प्रबंधित करना आसान बनाते हैं, और सिंक्रनाइज़ेशन की आवश्यकता को बहुत कम कर सकते हैं।
परमाणु चर में ऐसी विशेषताएं होती हैं जो सिंक्रनाइज़ेशन को कम करती हैं और मेमोरी संगतता त्रुटियों से बचने में मदद करती हैं।
एक सरल उदाहरण प्रदान करें जहां AtomicReference का उपयोग किया जाना चाहिए।
के साथ नमूना कोड AtomicReference
:
String initialReference = "value 1";
AtomicReference<String> someRef =
new AtomicReference<String>(initialReference);
String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);
क्या सभी बहुआयामी कार्यक्रमों में वस्तुओं को बनाने की आवश्यकता है?
आपको AtomicReference
सभी बहु थ्रेडेड प्रोग्राम में उपयोग करने की आवश्यकता नहीं है ।
यदि आप किसी एकल चर की रक्षा करना चाहते हैं, तो उपयोग करें AtomicReference
। यदि आप एक कोड ब्लॉक की रक्षा करना चाहते हैं, तो अन्य निर्माण जैसे Lock
/ synchronized
आदि का उपयोग करें ।
एक और सरल उदाहरण एक सत्र वस्तु में एक सुरक्षित धागा संशोधन करना है।
public PlayerScore getHighScore() {
ServletContext ctx = getServletConfig().getServletContext();
AtomicReference<PlayerScore> holder
= (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
return holder.get();
}
public void updateHighScore(PlayerScore newScore) {
ServletContext ctx = getServletConfig().getServletContext();
AtomicReference<PlayerScore> holder
= (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
while (true) {
HighScore old = holder.get();
if (old.score >= newScore.score)
break;
else if (holder.compareAndSet(old, newScore))
break;
}
}
स्रोत: http://www.ibm.com/developerworks/library/j-jtp09238/index.html