জাভাতে অপেক্ষা () এবং বিজ্ঞপ্তি () ব্যবহার করে একটি সাধারণ দৃশ্য


181

আমি কি একটি সম্পূর্ণ সাধারণ দৃশ্য অর্থাত্‍ টিউটোরিয়াল পেতে পারি যা এটি কীভাবে ব্যবহার করা উচিত তা বোঝায়, বিশেষত একটি সারি দিয়ে?

উত্তর:


269

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

আপনাকে প্রথমে যে কাজটি করতে হবে তা হ'ল শর্তগুলি সনাক্ত করা। এই ক্ষেত্রে, আপনি put()স্টোরে মুক্ত স্থান না পাওয়া পর্যন্ত পদ্ধতিটি অবরুদ্ধ করতে চান এবং take()কোনও উপাদান ফিরে না আসা পর্যন্ত আপনি এই পদ্ধতিটি অবরুদ্ধ করতে চান ।

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while(queue.size() == capacity) {
            wait();
        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}

আপনাকে যেভাবে ওয়েটটি ব্যবহার করতে হবে এবং প্রক্রিয়াগুলি অবহিত করতে হবে সে সম্পর্কে কয়েকটি বিষয় লক্ষ্যণীয়।

প্রথমত, আপনাকে নিশ্চিত করতে হবে যে কোনও কল wait()বা notify()কোডের একটি সিঙ্ক্রোনাইজড অঞ্চলের মধ্যে রয়েছে ( একই সাথে একই সাথে wait()notify()কলগুলি সিঙ্ক্রোনাইজ হচ্ছে)। এর কারণ (স্ট্যান্ডার্ড থ্রেড সুরক্ষা উদ্বেগ ব্যতীত) একটি মিসড সিগন্যাল হিসাবে পরিচিত এমন কোনও কারণ।

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

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

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


অন্যান্য উত্তরগুলির মধ্যে যেমন উল্লেখ করা হয়েছে, জাভা 1.5 একটি নতুন সমাবর্তন গ্রন্থাগার ( java.util.concurrentপ্যাকেজে) প্রবর্তন করেছে যা অপেক্ষা / বিজ্ঞপ্তি ব্যবস্থার উপর একটি উচ্চ স্তরের বিমূর্ততা সরবরাহ করার জন্য তৈরি করা হয়েছিল। এই নতুন বৈশিষ্ট্যগুলি ব্যবহার করে, আপনি মূল উদাহরণটি আবার লিখতে পারেন:

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T element) throws InterruptedException {
        lock.lock();
        try {
            while(queue.size() == capacity) {
                notFull.await();
            }

            queue.add(element);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while(queue.isEmpty()) {
                notEmpty.await();
            }

            T item = queue.remove();
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

অবশ্যই যদি আপনার যদি অবরুদ্ধ সারির প্রয়োজন হয় তবে আপনার ব্লকিংকিউ ইন্টারফেস প্রয়োগ করা উচিত ।

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


7
@ গ্রেউজে, notifyকেবল একটি সুতো জোগায় । দুটি গ্রাহক থ্রেড যদি কোনও উপাদানটি সরানোর জন্য প্রতিযোগিতা করে থাকে তবে একটি বিজ্ঞপ্তি অন্য গ্রাহক থ্রেডকে জাগ্রত করতে পারে যা এ সম্পর্কে কিছুই করতে পারে না এবং ঘুমাতে ফিরে যাবে (প্রযোজকের পরিবর্তে, আমরা আশা করছিলাম যে কোনও নতুন উপাদান সন্নিবেশ করানো হবে।) কারণ প্রযোজক থ্রেড জাগ্রত হয় না, কিছুই sertedোকানো হয় না এবং এখন তিনটি থ্রেড অনির্দিষ্ট সময়ের জন্য ঘুমাবে। আমি আমার পূর্ববর্তী মন্তব্যটি সরিয়ে দিয়েছি কারণ এটি (ভুলভাবে) বলেছিল যে
উত্সাহী জাগরণ

1
@ ফিন্ব্য যতদূর আমি বলতে পারি, আপনি যে সমস্যার সমাধান করেছেন তা নোটিফিয়েল () ব্যবহার করে সমাধান করা যেতে পারে। আমি কি সঠিক?
ক্লিন্ট ইস্টউড

1
@ জেয়ার্ড দ্বারা এখানে দেওয়া উদাহরণটি বেশ ভাল তবে মারাত্মক পতন হয়েছে। কোডে সমস্ত পদ্ধতি সিঙ্ক্রোনাইজড হিসাবে চিহ্নিত করা হয়েছে, তবে একই সময়ে দুটি সিঙ্ক্রোনাইজড পদ্ধতি ব্যবহার করা যাবে না, তবে কীভাবে ছবিতে দ্বিতীয় থ্রেড আসে।
শিবম আগরওয়াল

10
@ ব্রুট3 ফোরসি 3 আপনার অপেক্ষা ()) জাভাদোক পড়তে হবে: এটি বলে: থ্রেডটি এই মনিটরের মালিকানা প্রকাশ করে । সুতরাং, অপেক্ষা () কল করার সাথে সাথে মনিটরটি প্রকাশ করা হবে এবং অন্য একটি থ্রেড কাতার আরেকটি সুসংগত পদ্ধতি কার্যকর করতে পারে।
জেবি নিজেট

1
@JBNizet। "এর একটি উদাহরণ, একটি থ্রেড পুট কল করতে পারে () যখন সারিটি পূর্ণ হয়ে যায় তখন এটি শর্তটি পরীক্ষা করে, দেখায় যে সারিটি পূর্ণ রয়েছে, তবে এটি অন্য থ্রেডটি ব্লক করার আগে নির্ধারিত হয়েছে"। এখানে কীভাবে আসুন দ্বিতীয় থ্রেডটি নির্ধারিত হয়েছে যদি এখনও অপেক্ষা না করা হয়
শিবম আগরওয়াল

148

একটি সারি উদাহরণ নয়, তবে অত্যন্ত সহজ :)

class MyHouse {
    private boolean pizzaArrived = false;

    public void eatPizza(){
        synchronized(this){
            while(!pizzaArrived){
                wait();
            }
        }
        System.out.println("yumyum..");
    }

    public void pizzaGuy(){
        synchronized(this){
             this.pizzaArrived = true;
             notifyAll();
        }
    }
}

কিছু গুরুত্বপূর্ণ বিষয়:
1) কখনও করবেন না

 if(!pizzaArrived){
     wait();
 }

সর্বদা ব্যবহার (শর্ত), কারণ

  • ক) থ্রেডগুলি কারও দ্বারা অবহিত না হয়ে অপেক্ষারত অপেক্ষার স্থানে জাগ্রত হতে পারে। (এমনকি পিজ্জা লোক যখন চিম বাজেনি তখনও কেউ সিদ্ধান্ত নেবে যে পিজ্জা খাওয়ার চেষ্টা করবে))
  • খ) সিঙ্ক্রোনাইজড লকটি অর্জন করার পরে আপনার আবার শর্তটি পরীক্ষা করা উচিত। বলি পিজ্জা চিরকাল স্থায়ী হয় না। আপনি জেগে উঠুন, পিজ্জার জন্য লাইন আপ করুন, তবে এটি প্রত্যেকের পক্ষে যথেষ্ট নয়। আপনি যদি পরীক্ষা না করেন, আপনি কাগজ খেতে পারেন! :) (সম্ভবত আরও ভাল উদাহরণ হবে while(!pizzaExists){ wait(); }

২) অপেক্ষা / নোফটি চাওয়ার আগে আপনাকে অবশ্যই লকটি (সিঙ্ক্রোনাইজড) রাখতে হবে। জাগ্রত হওয়ার আগে থ্রেডগুলিও লক অর্জন করতে হয়।

3) আপনার সিঙ্ক্রোনাইজড ব্লকের মধ্যে কোনও লক অর্জন এড়ানোর চেষ্টা করুন এবং ভিনগ্রহী পদ্ধতিগুলি (যে পদ্ধতিগুলি আপনি কী জানেন তারা কী করছেন তা নিশ্চিতভাবে জানেন না) না চালানোর চেষ্টা করুন। আপনার যদি তা করতে হয় তবে ডেডলকগুলি এড়াতে ব্যবস্থা নেওয়ার বিষয়টি নিশ্চিত করুন।

4) বিজ্ঞপ্তি () সাথে সতর্কতা অবলম্বন করুন। আপনি কী করছেন তা না জানা পর্যন্ত notifyAll () এর সাথে লেগে থাকুন।

5) সর্বশেষ, তবে কম নয়, অনুশীলনে জাভা কনকুরেন্সি পড়ুন !


1
আপনি যদি দয়া করে "যদি (! পিজ্জাআরাইভড) {অপেক্ষা ();}" ব্যবহার করবেন না কেন তা ব্যাখ্যা করতে পারেন?
প্রত্যেকে 11

2
@ প্রত্যেকটি: কিছু ব্যাখ্যা যুক্ত করেছেন। আছে HTH।
এন্নো শিজিজি

1
pizzaArrivedপতাকা ব্যবহার করছেন কেন ? পতাকাটি যদি কোনও কল ছাড়াই পরিবর্তন notifyকরা হয় তবে এর কোনও প্রভাব থাকবে না। এছাড়াও কেবল উদাহরণ দিয়ে কাজ করে waitএবং notifyকল করে।
পাবলো ফার্নান্দেজ

2
আমি বুঝতে পারি না - থ্রেড 1 ইটপিজা () পদ্ধতিটি কার্যকর করে এবং শীর্ষের সিঙ্ক্রোনাইজড ব্লকে প্রবেশ করে এবং মাইহাউস ক্লাসে সিঙ্ক্রোনাইজ করে। কোনও পিজ্জা এখনও আসে নি তাই এটি অপেক্ষা করে। থ্রেড 2 পিজ্জাগু () পদ্ধতিতে কল করে পিজ্জা সরবরাহ করার চেষ্টা করে; তবে থ্রেড 1 ইতিমধ্যে লকের মালিক হিসাবে এটি করতে পারে না এবং এটি ছেড়ে দিচ্ছে না (এটি চিরকাল অপেক্ষা করছে)। কার্যকরভাবে ফলাফলটি একটি অচলাবস্থা - থ্রেড 1 নোটিফএল () পদ্ধতিটি চালানোর জন্য থ্রেড 2 এর জন্য অপেক্ষা করছে, যখন থ্রেড 2 মাইহাউস ক্লাসে লকটি ছেড়ে দেওয়ার জন্য থ্রেড 1 এর জন্য অপেক্ষা করছে ... আমি কী অনুপস্থিত তা কি? এখানে?
ফ্লেমিং_পিথন

1
না, যখন কোনও ভেরিয়েবলটি synchronizedকীওয়ার্ড দ্বারা সুরক্ষিত থাকে, তখন ভেরিয়েবলটি ঘোষণা করা volatileবাড়াবাড়ি হয় এবং বিভ্রান্তি এড়াতে এড়াতে বাঞ্ছনীয় @ মিরিদা
এন্নো শিওজি

37

যদিও আপনি চেয়েছিলেন wait()এবংnotify() বিশেষভাবে আমার কাছে মনে হয়েছে যে এই উদ্ধৃতিটি এখনও যথেষ্ট গুরুত্বপূর্ণ:

জোশ ব্লচ, কার্যকর জাভা 2 য় সংস্করণ , আইটেম 69: সম্মতিযুক্ত ইউটিলিটিগুলিতে পছন্দ করুন waitএবং notify(তার উপর জোর দিন):

ব্যবহার করতে waitএবং notifyসঠিকভাবে অসুবিধা দেওয়া , আপনার পরিবর্তে উচ্চ স্তরের সম্মিলিত ইউটিলিটিগুলি ব্যবহার করা উচিত [...] ব্যবহার করা waitএবং notifyসরাসরি প্রদত্ত উচ্চ-স্তরের ভাষার তুলনায় "কনক্যুরঞ্জি এসেম্বলি ল্যাঙ্গুয়েজে" প্রোগ্রামিংয়ের মতো java.util.concurrentখুব কমই ব্যবহৃত হয়, যদি কখনও হয় তবে ব্যবহার করার কারণ waitএবং notifyনতুন কোডে


Java.util.concurrent প্যাকেজে প্রদত্ত ব্লকিংকিউগুলি স্থির নয়। যখন সারি অবিরাম থাকা দরকার তখন আমরা কী ব্যবহার করতে পারি? অর্থাত্ যদি সিস্টেমটি কাতারে 20 আইটেম নিয়ে নেমে যায় তবে সিস্টেমটি পুনরায় চালু হওয়ার সাথে সাথে আমার উপস্থিত থাকতে হবে। যেহেতু java.util.conc موجودہ সারিগুলি সমস্ত 'মেমরিরূপে' উপস্থিত রয়েছে তা কেবল উপস্থিত থাকার জন্য দৃ pers়তা সক্ষম এমন বাস্তবায়ন সরবরাহ করার জন্য / হ্যাক / ওভাররাইড হিসাবে ব্যবহার করার কোনও উপায় আছে কি?
ভলক্সম্যান

1
সম্ভবত ব্যাকিং সারি সরবরাহ করা যেতে পারে? অর্থাৎ আমরা একটি সারি ইন্টারফেস বাস্তবায়ন সরবরাহ করব যা অবিচল।
ভলক্সম্যান

এই প্রেক্ষাপটে উল্লেখিত খুব ভালো যা আপনি ব্যবহার করার প্রয়োজন নেই হবে notify()এবং wait()আবার
চাকলাদার Asfak আরেফি

7

আপনি এই জাভা টিউটোরিয়ালটি একবার দেখেছেন ?

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

এটা আমি অন্তত কি করি। আমি কোনও সহকর্মী বিশেষজ্ঞ নই তাই আমি যেখানেই সম্ভব হাতের থ্রেড পরিচালনা করা থেকে দূরে থাকি।


2

উদাহরণ

public class myThread extends Thread{
     @override
     public void run(){
        while(true){
           threadCondWait();// Circle waiting...
           //bla bla bla bla
        }
     }
     public synchronized void threadCondWait(){
        while(myCondition){
           wait();//Comminucate with notify()
        }
     }

}
public class myAnotherThread extends Thread{
     @override
     public void run(){
        //Bla Bla bla
        notify();//Trigger wait() Next Step
     }

}

0

থ্রেডিংয়ে অপেক্ষা () এবং বিজ্ঞপ্তি () এর উদাহরণ।

একটি সিঙ্ক্রোনাইজড স্ট্যাটিক অ্যারে তালিকাটি রিসোর্স হিসাবে ব্যবহৃত হয় এবং অ্যারের তালিকাটি ফাঁকা থাকলে অপেক্ষা () পদ্ধতিটি বলা হয়। অ্যারে তালিকার জন্য কোনও উপাদান যুক্ত হয়ে গেলে सूचित করুন () পদ্ধতিটি চালু করা হয়।

public class PrinterResource extends Thread{

//resource
public static List<String> arrayList = new ArrayList<String>();

public void addElement(String a){
    //System.out.println("Add element method "+this.getName());
    synchronized (arrayList) {
        arrayList.add(a);
        arrayList.notifyAll();
    }
}

public void removeElement(){
    //System.out.println("Remove element method  "+this.getName());
    synchronized (arrayList) {
        if(arrayList.size() == 0){
            try {
                arrayList.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            arrayList.remove(0);
        }
    }
}

public void run(){
    System.out.println("Thread name -- "+this.getName());
    if(!this.getName().equalsIgnoreCase("p4")){
        this.removeElement();
    }
    this.addElement("threads");

}

public static void main(String[] args) {
    PrinterResource p1 = new PrinterResource();
    p1.setName("p1");
    p1.start();

    PrinterResource p2 = new PrinterResource();
    p2.setName("p2");
    p2.start();


    PrinterResource p3 = new PrinterResource();
    p3.setName("p3");
    p3.start();


    PrinterResource p4 = new PrinterResource();
    p4.setName("p4");
    p4.start();     

    try{
        p1.join();
        p2.join();
        p3.join();
        p4.join();
    }catch(InterruptedException e){
        e.printStackTrace();
    }
    System.out.println("Final size of arraylist  "+arrayList.size());
   }
}

1
plz ডাবল এই লাইনটি পরীক্ষা করুন if(arrayList.size() == 0), আমি মনে করি এটি এখানে ভুল হতে পারে।
উইজম্যান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.