আমরা কখন ব্যবহার করব 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
, যাতে কম্পাইলার সে অনুযায়ী নিখুত পারবেন না।
যখন আপনাকে একাধিক থ্রেডের মধ্যে একটি অপরিবর্তনীয় বস্তুর অবস্থা ভাগ করে নেওয়া এবং পরিবর্তন করতে হয় তখন একটি পারমাণবিক রেফারেন্স ব্যবহার করা আদর্শ। এটি একটি সুপার ঘন বিবরণ তাই আমি এটি কিছুটা ভেঙে দেব।
প্রথমত, একটি অপরিবর্তনীয় বস্তু এমন একটি বস্তু যা নির্মাণের পরে কার্যকরভাবে পরিবর্তন হয় না। প্রায়শই কোনও পরিবর্তনযোগ্য অবজেক্টের পদ্ধতিগুলি একই শ্রেণীর নতুন দৃষ্টান্তগুলি দেয়। কয়েকটি উদাহরণের মধ্যে লং এবং ডাবলের মোড়ক ক্লাসের পাশাপাশি স্ট্রিংয়ের কয়েকটি অন্তর্ভুক্ত রয়েছে। ( জেভিএম অপরিবর্তনীয় বস্তুগুলিতে প্রোগ্রামিং কনকুরেন্সির মতে আধুনিক সম্মিলনের একটি সমালোচনা অঙ্গ)।
পরবর্তী, কেন পারমাণবিক রেফারেন্স সেই ভাগ করা মানটি ভাগ করে নেওয়ার জন্য একটি উদ্বায়ী বস্তুর চেয়ে ভাল। একটি সাধারণ কোড উদাহরণ পার্থক্য প্রদর্শন করবে।
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);
}
এখন কেন এই ভাল? সত্যি বলতে যে কোডটি আগের চেয়ে একটু কম পরিষ্কার clean তবে সত্যই গুরুত্বপূর্ণ কিছু রয়েছে যা অ্যাটমিকরফ্রান্সের হুডের নীচে ঘটে এবং এটি তুলনা এবং অদলবদল হয়। এটি কোনও সিপিইউ নির্দেশনা, কোনও ওএস কল নয়, যা স্যুইচটি ঘটায়। এটি সিপিইউতে একক নির্দেশনা। এবং কোনও লক না থাকায় লকটি প্রয়োগ করা হয় এমন ক্ষেত্রে কোনও প্রসঙ্গের স্যুইচ নেই যা আরও বেশি সময় সাশ্রয় করে!
ক্যাচটি হ'ল, অ্যাটমিকরাইফারেন্সের জন্য এটি কোনও .equals () কলটি ব্যবহার করে না, পরিবর্তে একটি == প্রত্যাশিত মানের তুলনা করে। সুতরাং নিশ্চিত হয়ে নিন যে প্রত্যাশিতটি আসল বস্তুটি লুপ থেকে ফিরে আসার পরে।
worked
একই শব্দার্থবিজ্ঞান পেতে আপনাকে লুপ করতে হবে।
এটমিক রেফারেন্সের জন্য এখানে একটি ব্যবহারের কেস:
এই শ্রেণিটি বিবেচনা করুন যা একটি সংখ্যা ব্যাপ্তি হিসাবে কাজ করে এবং নিম্ন এবং উপরের সংখ্যার সীমা বজায় রাখার জন্য স্বতন্ত্র 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
একটি বিকল্প :
public void doSomethingUsingLambdas() {
AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();
soSomethingThatTakesALambda(() -> {
yourObjectRef.set(youObject);
});
soSomethingElseThatTakesALambda(() -> {
YourObject yourObject = yourObjectRef.get();
});
}
আমি এটি বলছি না এটি ভাল নকশা বা কোনও কিছু (এটি কেবলমাত্র একটি তুচ্ছ উদাহরণ), তবে আপনার যদি ল্যাম্বদা আমন্ত্রণের মধ্যে একটি বিষয় ভাগ করার প্রয়োজন হয় তবে আপনার AtomicReference
এটি একটি বিকল্প।
প্রকৃতপক্ষে আপনি যে কোনও অবজেক্ট ব্যবহার করতে পারেন যা রেফারেন্স ধারণ করে এমনকি এমন একটি সংগ্রহ যা কেবল একটি আইটেম রয়েছে has তবে এটমিক রেফারেন্স একটি নিখুঁত ফিট।
আমি বেশি কথা বলব না। ইতিমধ্যে আমার শ্রদ্ধেয় সহকর্মীরা তাদের মূল্যবান ইনপুট দিয়েছেন।এই ব্লগের শেষের সম্পূর্ণ চলমান কোডের যে কোনও বিভ্রান্তি দূর করা উচিত। এটি মাল্টি-থ্রেড দৃশ্যে একটি মুভি সিট বুকিং ছোট প্রোগ্রাম সম্পর্কে।
কয়েকটি গুরুত্বপূর্ণ প্রাথমিক তথ্য নিম্নরূপ: 1> বিভিন্ন থ্রেড কেবলমাত্র উদাহরণস্বরূপ এবং স্তূপ স্থানে স্থির সদস্যের ভেরিয়েবলের পক্ষে প্রার্থনা করতে পারে। 2> অস্থির পড়া বা লেখা সম্পূর্ণ পরমাণু এবং সিরিয়ালাইজড হয় / ঘটে এবং কেবল মেমরি থেকে সম্পন্ন হয়। এটি বলে আমার অর্থ হ'ল যে কোনও পাঠ্য স্মৃতিতে পূর্বের লেখাকে অনুসরণ করবে। এবং যে কোনও লেখা মেমরি থেকে পূর্ববর্তী পড়তে অনুসরণ করবে। সুতরাং কোনও অস্থির সাথে কাজ করা কোনও থ্রেড সর্বদা সর্বাধিক আপ টু ডেট মান দেখতে পাবে। অ্যাটমিক রেফারেন্সটি অস্থির এই সম্পত্তিটি ব্যবহার করে।
অ্যাটমিকরেফারেন্সের উত্স কোডের কয়েকটি নীচে দেওয়া হয়েছে। অ্যাটমিক রেফারেন্স একটি অবজেক্ট রেফারেন্সকে বোঝায়। এই রেফারেন্সটি নীচে হিসাবে পারমাণবিক উল্লেখের ক্ষেত্রে একটি অস্থায়ী সদস্য পরিবর্তনশীল।
private volatile V value;
get () সহজেই পরিবর্তনশীলটির সর্বশেষতম মানটি ফেরত দেয় (যেমন "উদ্বৃত্ত একটি" আগে ঘটে "পদ্ধতিতে ঘটে)।
public final V get()
নিম্নলিখিতটি হল অ্যাটমিক রেফারেন্সের সবচেয়ে গুরুত্বপূর্ণ পদ্ধতি।
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
তুলনাআন্ডসেট (প্রত্যাশা, আপডেট) পদ্ধতিটি জাভা এর অনিরাপদ শ্রেণীর তুলনাআন্ডস্বেপঅবজেক্ট () পদ্ধতিটিকে কল করে। অনিরাপদে থাকা এই পদ্ধতির কলটি নেটিভ কলকে আমন্ত্রণ জানায়, যা প্রসেসরের কাছে একক নির্দেশনা আহ্বান করে। প্রতিটি রেফারেন্সকে "প্রত্যাশা" এবং "আপডেট" করুন object
যদি এবং কেবলমাত্র যদি অ্যাটমিকরেফারেন্স ইনস্ট্যান্সের সদস্য ভেরিয়েবল "মান" একই বস্তুকে বোঝায় "প্রত্যাশা" দ্বারা চিহ্নিত করা হয়, "আপডেট" এই ইনস্ট্যান্স ভেরিয়েবলকে এখন বরাদ্দ করা হয়, এবং "সত্য" ফিরে আসে। অন্যথায়, মিথ্যা ফিরিয়ে দেওয়া হয়। পুরো জিনিসটি পারমাণবিকভাবে সম্পন্ন হয়। এর মধ্যে আর কোনও থ্রেড বাধা দিতে পারে না। যেহেতু এটি একটি একক প্রসেসর অপারেশন (আধুনিক কম্পিউটার আর্কিটেকচারের যাদু), এটি একটি সিঙ্ক্রোনাইজড ব্লক ব্যবহার করার চেয়ে প্রায়শই দ্রুত। কিন্তু মনে রাখবেন যে যখন একাধিক ভেরিয়েবলগুলি পরমাণুভাবে আপডেট করা দরকার তখন অ্যাটমিকরেফারেন্স সাহায্য করবে না।
আমি একটি পূর্ণাঙ্গ রানিং কোড যুক্ত করতে চাই, যা গ্রহনে চালানো যেতে পারে। এটি অনেক গুলিয়ে ফেলবে। এখানে 22 জন ব্যবহারকারী (MyTh থ্রেড) 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
একক ভেরিয়েবলগুলিতে লক-ফ্রি থ্রেড-নিরাপদ প্রোগ্রামিং সমর্থন করে।
উচ্চ স্তরের সমকালীন API সহ থ্রেড সুরক্ষা অর্জনের একাধিক উপায় রয়েছে । পারমাণবিক ভেরিয়েবল একাধিক বিকল্পের মধ্যে একটি।
Lock
অবজেক্টগুলি লকিং আইডিয়ামগুলিকে সমর্থন করে যা অনেকগুলি সমবর্তী অ্যাপ্লিকেশনকে সহজতর করে।
Executors
থ্রেড চালু এবং পরিচালনা করার জন্য একটি উচ্চ-স্তরের এপিআই সংজ্ঞা দিন। Java.util.concurrent দ্বারা প্রদত্ত এক্সিকিউটার বাস্তবায়ন বৃহত-স্কেল অ্যাপ্লিকেশনগুলির জন্য উপযুক্ত থ্রেড পুল পরিচালনা সরবরাহ করে।
সমকালীন সংগ্রহগুলি বড় আকারের ডেটা সংগ্রহগুলি পরিচালনা করা সহজ করে তোলে এবং সিঙ্ক্রোনাইজেশনের প্রয়োজনীয়তা ব্যাপকভাবে হ্রাস করতে পারে।
পারমাণবিক ভেরিয়েবলগুলির এমন বৈশিষ্ট্য রয়েছে যা সিঙ্ক্রোনাইজেশন হ্রাস করে এবং মেমরির ধারাবাহিকতা ত্রুটিগুলি এড়াতে সহায়তা করে।
একটি সাধারণ উদাহরণ সরবরাহ করুন যেখানে অ্যাটমিক রেফারেন্স ব্যবহার করা উচিত।
এর সাথে নমুনা কোড 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