2

以下のコードは、循環キューにランダムな値を挿入して削除しようとしています。ただし、同期の問題がいくつかあります。私はより高いレベルのルーチンを使用できることを知っており、製品コードでそれを行うつもりですが、なぜこれが機能しないのか不思議でした。ここで何が欠けていますか?

public class CircularQueue {
int count;
int rear;
int front;
Object lock = new Object();
int size;
int[] array;
CircularQueue(int size)
{
    this.size= size;
    array = new int[size];
}

void enqueue(int number) throws InterruptedException
{
    if(isFull())
        lock.wait();

    synchronized(lock)
    {

        array[rear] = number;
        System.out.println("Rear is:"+ rear+ "value is:"+number+"Size is:"+size);

        rear = (rear+1)%size;
        count++;
    }
    lock.notify();

}

void dequeue() throws InterruptedException
{
    if(isEmpty())
        lock.wait();

    synchronized(lock)
    {
        int retVal = 0;
        retVal = array[front];
        System.out.println("Front is:"+ front+ "value is:"+retVal);

        front = (front+1)%size;
        count--;
    }

    lock.notify();

}

boolean isFull()
{
    if(count == size)
    {
        return true;
    }
    else
        return false;

}

boolean isEmpty()
{
    return count == 0;
}
}

// テストクラス

import java.util.Random;
public class App {

    public static void main(String[] args) throws InterruptedException
    {
       final Random random = new Random();
       final CircularQueue circularQueue = new CircularQueue(10);
       Thread t1 = new Thread(new Runnable(){

        @Override
        public void run() {
            try {
                circularQueue.enqueue(random.nextInt(100));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

       });
       Thread t2 = new Thread(new Runnable(){

            @Override
            public void run() {
                try {
                    circularQueue.dequeue();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

           });

       t1.start();
       t2.start();
       t1.join();
       t2.join();

    }

}
4

2 に答える 2

6

java.lang.Object#waitjava.lang.Object#notifyjava.lang.Object#notifyAllは同期ブロックから呼び出す必要があるためです。

解決策として(確認する必要があります)、条件を同期ブロック内に配置する必要があります。

void enqueue(int number) throws InterruptedException
{

    synchronized(lock)
    {
        if(isFull())
           lock.wait();

        array[rear] = number;
        System.out.println("Rear is:"+ rear+ "value is:"+number+"Size is:"+size);

        rear = (rear+1)%size;
        count++;
        lock.notify();
    }
}

void dequeue() throws InterruptedException
{
    synchronized(lock)
    {
        if(isEmpty())
           lock.wait();

        int retVal = 0;
        retVal = array[front];
        System.out.println("Front is:"+ front+ "value is:"+retVal);

        front = (front+1)%size;
        count--;
        lock.notify();
    }

}
于 2012-11-11T21:00:33.847 に答える
0

このコードのもう 1 つの問題は、isEmpty/isFull が true を返したとしても、隣接する待機を呼び出すまで、キューの状態が変化する可能性があることです。
例:
- キューは空です
- スレッド 1 は isEmpty() を呼び出します
- コンテキスト スイッチ
- スレッド 2 はエンキューを呼び出します (キューは空ではありません)
- コンテキスト スイッチ
- キューは空ではありませんが、スレッド 1 は lock.wait() イベントを呼び出しません

もちろん、この問題は、wait()/notify() の呼び出しが同期ブロックに配置されるときに解決されます。

于 2012-11-11T21:04:13.433 に答える