كيفية استخدام الانتظار والإخطار في جافا دون غير قانوني

StackOverflow https://stackoverflow.com/questions/886722

سؤال

لدي 2 مصفوفة وأحتاج إلى ضربها ثم طباعة نتائج كل خلية. بمجرد أن تكون خلية واحدة جاهزة، أحتاج إلى طباعتها، ولكن على سبيل المثال، أحتاج إلى طباعة خلية [0] [0] قبل الخلية [2] [0] حتى لو كانت نتيجة [2] [0] جاهز أولا وبعد لذلك أنا بحاجة إلى طباعة ذلك بالترتيب. لذلك فكرتي هي جعل موضوع الطابعة تنتظر حتى multiplyThread يخطر ذلك أن الخلية الصحيحة جاهزة للطباعة ثم printerThread سوف تطبع الخلية والعودة إلى الانتظار وهلم جرا ..

لذلك لدي هذا الموضوع الذي يقوم الضرب:

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

خيط يطبع نتيجة كل خلية:

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

الآن يرمي لي هذه الاستثناءات:

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

خط 49 في multiplyThread هو "إخطار ()" .. أعتقد أنني بحاجة إلى استخدام المتزامنة بشكل مختلف، لكنني لست متأكدا من كيفية ذلك.

إذا كان بإمكان أي شخص مساعدة هذا الرمز للعمل سأقدر ذلك حقا.

هل كانت مفيدة؟

المحلول

لتكون قادرة على الاتصال إعلام () تحتاج إلى مزامنة على نفس الكائن.

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}

نصائح أخرى

أثناء استخدام wait و notify أو notifyAll طرق في Java يجب تذكر الأشياء التالية:

  1. يستخدم notifyAll بدلا من notify إذا كنت تتوقع أن يكون أكثر من موضوع واحد ينتظر قفل.
  2. ال wait و notify يجب أن يتم استدعاء الطرق في سياق متزامن. وبعد انظر الرابط للحصول على شرح أكثر تفصيلا.
  3. استدعاء دائما wait() الطريقة في حلقة لأنه إذا كان هناك خيوط متعددة في انتظار قفل واحد منهم حصل على القفل وإعادة ضبط الشرط، فحتاج المواضيع الأخرى للتحقق من الشرط بعد الاستيقاظ لمعرفة ما إذا كانوا بحاجة إلى الانتظار مرة أخرى أو يمكن بدء المعالجة وبعد
  4. استخدم نفس الكائن للاتصال wait() و notify() طريقة؛ كل كائن لديه قفل خاص به wait() على الكائن A و notify() على كائن B لن يجعل أي معنى.

هل تحتاج إلى موضوع هذا على الإطلاق؟ أتساءل مدى حجم المصفوفات الخاصة بك، وما إذا كانت هناك أي فائدة في الحصول على طباعة مؤشر ترابط واحد أثناء وجود الآخر الضرب.

ربما يكون الأمر يستحق قياس هذا الوقت قبل القيام بعمل الخيوط المعقدة نسبيا؟

إذا كنت بحاجة إلى مؤشر ترابطه، فسأخلق مؤشرات الترابط "N" لإجراء الضرب للخلايا (ربما "N 'هو عدد النوى المتاح لك)، ثم استخدم ExecutorService. و مستقبل آلية لإرسال تضاعفات متعددة في وقت واحد.

وبهذه الطريقة يمكنك تحسين العمل بناء على عدد النوى، وأنت تستخدم أدوات خيوط Java ذات المستوى الأعلى (والتي يجب أن تجعل الحياة أسهل). اكتب النتائج مرة أخرى في مصفوفة استقبال، ثم قم ببساطة بطباعة هذا بمجرد الانتهاء من جميع مهامك المستقبلية.

دعنا نقول أن لديك تطبيق "صندوق أسود" مع بعض الفئة المسماة BlackBoxClass هذا له طريقة doSomething();.

علاوة على ذلك، لديك مراقب أو مستمع يدعى onResponse(String resp) سيتم استدعاء ذلك BlackBoxClass بعد وقت غير معروف.

التدفق بسيط:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

دعنا نقول أننا لا نعرف ما يجري BlackBoxClass وعندما يجب أن نحصل على إجابة، لكنك لا ترغب في متابعة الكود الخاص بك حتى تحصل على إجابة أو في كلمة أخرى onResponse يتصل. هنا يدخل "مزامنة المساعد":

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

الآن يمكننا تنفيذ ما نريد:

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}

يمكنك فقط الاتصال بالخطر على الكائنات التي تملكها مشاهدتها. لذلك تحتاج إلى شيء مثل

synchronized(threadObject)
{
   threadObject.notify();
}

notify() يحتاج إلى مزامنة كذلك

أنا على حق مثال بسيط تظهر لك الطريقة الصحيحة للاستخدام wait و notify في جاوة. لذلك سوف أقوم بإنشاء طبيبين يدعى الخيوط & صطر. وبعد سوف thercea الاتصال بالحصى.

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

وللعصى الصفية:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }

يمكننا أن ندعو إشعار لاستئناف تنفيذ كائنات الانتظار كما

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

استئناف هذا من خلال استدعاء إخطار على كائن آخر من نفس الفئة

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

استخدام بسيط إذا كنت تريد كيفية تنفيذ المواضيع بدلا من ذلك: -

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}

استجابة :-

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

بالنسبة لهذه المشكلة الخاصة، لماذا لا تخزن نتائجك المختلفة الخاصة بك في المتغيرات، ثم عند معالجة آخر مؤشر ترابطك، يمكنك الطباعة بأي شكل تريده. هذا مفيد بشكل خاص إذا كنت ستستخدم تاريخ عملك في مشاريع أخرى.

هذا يبدو وكأنه موقف لنمط المستهلكين المنتجين. إذا كنت تستخدم Java 5 أو UP، فقد تفكر في استخدام قائمة انتظار حظر (Java.util.concurrent.blockingqueue) واترك أعمال تنسيق مؤشر الترابط إلى تنفيذ الإطار / API الأساسي. انظر المثال من Java 5:http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/blockingqueue.html.أو جافا 7 (نفس المثال):http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/blockingqueue.html.

لقد تحركت بشكل صحيح كتلة التعليمات البرمجية عند الاتصال wait() طريقة باستخدام synchronized(this).

لكنك لم تتخذ نفس الاحتياطات عند الاتصال notify() طريقة دون استخدام كتلة حراسة: synchronized(this) أو synchronized(someObject)

إذا قمت بالرجوع إلى صفحة وثائق Oracle موضوع فئة، والتي تحتوي wait() ,notify(), notifyAll() الأساليب، يمكنك أن ترى أدناه الاحتياطات في كل هذه الطرق الثلاث

يجب أن يتم استدعاء هذه الطريقة فقط بواسطة مؤشر ترابط هو مالك شاشة هذا الكائن

تم تغيير أشياء كثيرة في 7 سنوات الماضية ودعنا ننظر إلى بدائل أخرى ل synchronized في الأسفل SE:

لماذا استخدام Reentrantlock إذا كان بإمكان المرء استخدام مزامنة (هذا)؟

التزامن مقابل قفل

تجنب مزامنة (هذا) في جافا؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top