在Java中实现Peterson Lock

时间:2011-08-23 21:06:26

标签: java concurrency locking

我正在尝试在Java中实现Peterson's algorithm,并且暂时创建了以下内容

public class Peterson {
    private volatile boolean[] flag = new boolean[2];
    private volatile int victim;

    public void lock(int id)
    {
        //System.out.println(id);
        int i = id;
        int j = 1 - id; 
        flag[i] = true;
        victim = i;
        while (flag[j] && victim == i) {};
    }

    public void unlock(int id)
    {
        flag[id] = false;
    }
}

我得到以下代码来测试锁...

class Counter {
    private int value;

    public Counter(int c)   {
        value = c;
    }

    public int get()
    {
        return value;
    }

    public int getAndIncrement()    {       
        return value++;
    }
}


class Thread1 implements Runnable   {
    private Counter c;
    private int id;
    private List<Integer> values;
    private Peterson lock;

    public Thread1(Counter c, int id, List<Integer> values, Peterson l) {
        this.c = c;
        this.id = id;
        this.values = values;
        this.lock = l;
    }

    public void run() {

        while (true)
        {
            lock.lock(id);
            try {
                try {

                    if (c.get() > 20000)
                        return;

                    int n = c.getAndIncrement();
                    values.add(n);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            finally {
                lock.unlock(id);
            }
        }
    }
}

public class Tmp    {

    public static void main(String[] args) throws IOException   {

        Counter  c = new Counter(1);
        Thread[] t = new Thread[2];
        List<Integer> values = new ArrayList<Integer>();
        Peterson l =  new Peterson();

        for (int i = 0; i < t.length; ++i)  {
            t[i] = new Thread(new Thread1(c, i, values, l));
            t[i].start();
        }

        System.out.println(values.size());
    }
}

虽然我希望System.out.println(values.size());能够打印20000它会在每次运行时打印不同的数字。为什么是这样?我做错了什么?

4 个答案:

答案 0 :(得分:1)

当您解锁以保证在

之前发生时,您不会创建内存屏障

添加volatile boolean barr并在解锁时将其写为真,并将锁中的时间更改为while (barr && flag[j] && victim == i) {};

那你并没有等待你创建的主题

for (int i = 0; i < t.length; ++i)  {
    t[i] = new Thread(new Thread1(c, i, values, l));
    t[i].start();
}

for (int i = 0; i < t.length; ++i)  {
    try{
        t[i].join();
    }catch(InterruptedException e){
        Thread.currentThread().interrupt();//don't expect it but good practice to handle anyway
    }
}

答案 1 :(得分:0)

我想,这是因为Lock类中的lock()方法不是原子的。具体来说,此代码修改了两个内存地址,然后使用它们两个锁定来检查条件:

public void lock(int id)
{
    ...
    flag[i] = true;
    victim = i;
    while (flag[j] && victim == i) {};
}

如果在执行

时两个线程交错,算法是否有效
    flag[i] = true;
    victim = i;

其次,因为private volatile boolean[] flag = new boolean[2]将数组引用标记为volatile,而不是数组内容。

答案 2 :(得分:0)

正如其他人指出的那样,Java对数组元素没有易失性读/写。

您可以使用具有AtomicIntegerArrayget()的{​​{1}}带有易变的效果。

答案 3 :(得分:-2)

它不起作用,因为你使数组引用变为volatile,而不是内容(如已经指出的那样)。但是你总是可以在一个类中包含一个volatile字段并创建一个数组。