পারমাণবিক অপারেশনের উত্থানের জন্য পূর্বশর্ত

পারমাণবিক ক্রিয়াকলাপগুলি কীভাবে কাজ করে তা বোঝার জন্য এই উদাহরণটি একবার দেখে নেওয়া যাক:

public class Counter {
    int count;

    public void increment() {
        count++;
    }
}

যখন আমাদের একটি থ্রেড থাকে, তখন সবকিছু দুর্দান্ত কাজ করে, কিন্তু আমরা যদি মাল্টিথ্রেডিং যোগ করি, আমরা ভুল ফলাফল পাই এবং সব কারণ বৃদ্ধির অপারেশনটি একটি অপারেশন নয়, তিনটি: বর্তমান মান পাওয়ার জন্য একটি অনুরোধগণনা, তারপর এটি 1 দ্বারা বৃদ্ধি করুন এবং আবার লিখুনগণনা.

এবং যখন দুটি থ্রেড একটি পরিবর্তনশীল বৃদ্ধি করতে চায়, আপনি সম্ভবত ডেটা হারাবেন। অর্থাৎ, উভয় থ্রেড 100 পায়, ফলস্বরূপ, উভয়ই 102 এর প্রত্যাশিত মানের পরিবর্তে 101 লিখবে।

এবং কিভাবে সমাধান করবেন? আপনি লক ব্যবহার করতে হবে. সিঙ্ক্রোনাইজড কীওয়ার্ড এই সমস্যার সমাধান করতে সাহায্য করে, এটি ব্যবহার করে আপনাকে গ্যারান্টি দেয় যে একটি থ্রেড একবারে পদ্ধতিটি অ্যাক্সেস করবে।

public class SynchronizedCounterWithLock {
    private volatile int count;

    public synchronized void increment() {
        count++;
    }
}

এছাড়াও, আপনাকে উদ্বায়ী কীওয়ার্ড যোগ করতে হবে , যা থ্রেডের মধ্যে রেফারেন্সের সঠিক দৃশ্যমানতা নিশ্চিত করে। আমরা উপরে তার কাজ পর্যালোচনা করেছি।

কিন্তু এখনও downsides আছে. সবচেয়ে বড়টি হল পারফরম্যান্স, সেই সময়ে যখন অনেক থ্রেড একটি লক অর্জন করার চেষ্টা করছে এবং একটি লেখার সুযোগ পায়, বাকি থ্রেডগুলি হয় অবরুদ্ধ বা স্থগিত করা হবে যতক্ষণ না থ্রেডটি মুক্তি পায়।

এই সমস্ত প্রক্রিয়া, ব্লক করা, অন্য স্থিতিতে স্যুইচ করা সিস্টেমের কার্যকারিতার জন্য খুব ব্যয়বহুল।

পারমাণবিক অপারেশন

অ্যালগরিদম নিম্ন-স্তরের মেশিন নির্দেশাবলী ব্যবহার করে যেমন তুলনা-ও-অদলবদল (CAS, তুলনা-ও-অদলবদল, যা ডেটা অখণ্ডতা নিশ্চিত করে এবং সেগুলির উপর ইতিমধ্যেই প্রচুর পরিমাণে গবেষণা রয়েছে)।

একটি সাধারণ CAS অপারেশন তিনটি অপারেন্ডে কাজ করে:

  • কাজের জন্য মেমরি স্পেস (M)
  • একটি ভেরিয়েবলের বিদ্যমান প্রত্যাশিত মান (A)
  • নতুন মান (B) সেট করতে হবে

CAS পারমাণবিকভাবে M থেকে B আপডেট করে, কিন্তু শুধুমাত্র যদি M-এর মান A-এর মতো হয়, অন্যথায় কোনো ব্যবস্থা নেওয়া হয় না।

প্রথম এবং দ্বিতীয় ক্ষেত্রে, M এর মান ফেরত দেওয়া হবে। এটি আপনাকে তিনটি ধাপ একত্রিত করতে দেয়, যথা, মান পাওয়া, মান তুলনা করা এবং এটি আপডেট করা। এবং এটি সমস্ত মেশিন স্তরে একটি অপারেশনে পরিণত হয়।

যে মুহুর্তে একটি মাল্টি-থ্রেডেড অ্যাপ্লিকেশন একটি ভেরিয়েবল অ্যাক্সেস করে এবং এটি আপডেট করার চেষ্টা করে এবং CAS প্রয়োগ করা হয়, তখন থ্রেডগুলির মধ্যে একটি এটি পাবে এবং এটি আপডেট করতে সক্ষম হবে। কিন্তু লকগুলির বিপরীতে, অন্যান্য থ্রেডগুলি কেবল মান আপডেট করতে না পারার বিষয়ে ত্রুটি পাবে। তারপরে তারা আরও কাজের দিকে অগ্রসর হবে এবং এই ধরণের কাজের মধ্যে স্যুইচিং সম্পূর্ণভাবে বাদ দেওয়া হয়েছে।

এই ক্ষেত্রে, সিএএস অপারেশন সফলভাবে কাজ না করলে আমাদের পরিস্থিতি পরিচালনা করতে হবে এই কারণে যুক্তিটি আরও কঠিন হয়ে ওঠে। আমরা শুধু কোডটি মডেল করব যাতে অপারেশন সফল না হওয়া পর্যন্ত এটি অগ্রসর না হয়।

পারমাণবিক প্রকারের ভূমিকা

আপনি কি এমন পরিস্থিতির মুখোমুখি হয়েছেন যেখানে আপনাকে int- এর সহজতম পরিবর্তনশীলের জন্য সিঙ্ক্রোনাইজেশন সেট আপ করতে হবে ?

আমরা ইতিমধ্যে কভার করেছি প্রথম উপায় হল volatile + synchronized ব্যবহার করা । কিন্তু বিশেষ পারমাণবিক* ক্লাস আছে।

যদি আমরা CAS ব্যবহার করি, তাহলে প্রথম পদ্ধতির তুলনায় অপারেশনগুলি দ্রুত কাজ করে। এবং উপরন্তু, আমরা একটি মান যোগ করার জন্য বিশেষ এবং খুব সুবিধাজনক পদ্ধতি আছে এবং বৃদ্ধি এবং হ্রাস অপারেশন.

AtomicBoolian , AtomicInteger , AtomicLong , AtomicIntegerArray , AtomicLongArray হল এমন ক্লাস যেখানে অপারেশনগুলি পারমাণবিক। নীচে আমরা তাদের সাথে কাজ বিশ্লেষণ করব।

পারমাণবিক পূর্ণসংখ্যা

AtomicInteger ক্লাস একটি int মানের অপারেশন প্রদান করে যা পারমাণবিকভাবে পড়া এবং লেখা যায়, বর্ধিত পারমাণবিক অপারেশন প্রদানের পাশাপাশি।

এটিতে পাওয়া এবং সেট করার পদ্ধতি রয়েছে যা ভেরিয়েবল পড়া এবং লেখার মতো কাজ করে।

অর্থাৎ, "হবে-আগে" একই ভেরিয়েবলের পরবর্তী প্রাপ্তির সাথে যা আমরা আগে বলেছি। পারমাণবিক তুলনা এবং সেট পদ্ধতিতেও এই মেমরির সামঞ্জস্যতা বৈশিষ্ট্য রয়েছে।

একটি নতুন মান প্রদান করে এমন সমস্ত অপারেশন পারমাণবিকভাবে সঞ্চালিত হয়:

int addAndGet (int ডেল্টা) বর্তমান মানের সাথে একটি নির্দিষ্ট মান যোগ করে।
বুলিয়ান তুলনা এবং সেট (প্রত্যাশিত int, আপডেট int) বর্তমান মান যদি প্রত্যাশিত মানের সাথে মেলে তাহলে প্রদত্ত আপডেট করা মানটিতে মান সেট করে।
int decrementAndGet() বর্তমান মান এক করে হ্রাস করে।
int getAndAdd(int delta) বর্তমান মানের সাথে প্রদত্ত মান যোগ করে।
int getAndDecrement() বর্তমান মান এক করে হ্রাস করে।
int getAndIncrement() বর্তমান মান এক দ্বারা বৃদ্ধি করে।
int getAndSet(int newValue) প্রদত্ত মান সেট করে এবং পুরানো মান প্রদান করে।
int incrementAndGet() বর্তমান মান এক দ্বারা বৃদ্ধি করে।
lazySet(int newValue) অবশেষে প্রদত্ত মান সেট করুন।
বুলিয়ান দুর্বল CompareAndSet (প্রত্যাশিত, আপডেট int) বর্তমান মান যদি প্রত্যাশিত মানের সাথে মেলে তাহলে প্রদত্ত আপডেট করা মানটিতে মান সেট করে।

উদাহরণ:

ExecutorService executor = Executors.newFixedThreadPool(5);
IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet));
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);

System.out.println(atomicInteger.get()); // prints 50