用Java实现Peterson锁

用Java实现Peterson锁,java,concurrency,locking,Java,Concurrency,Locking,我正在尝试用Java实现,目前已经创建了以下内容 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;

我正在尝试用Java实现,目前已经创建了以下内容

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());
    }
}
类计数器{
私有int值;
公共柜台(INTC){
数值=c;
}
公共int get()
{
返回值;
}
public int getAndIncrement(){
返回值++;
}
}
类Thread1实现可运行的{
私人柜台c;
私有int-id;
私有列表值;
私人彼得森锁;
公共线程1(计数器c、int id、列表值、Peterson l){
这个.c=c;
this.id=id;
这个值=值;
这个锁=l;
}
公开募捐{
while(true)
{
锁。锁(id);
试一试{
试一试{
如果(c.get()>20000)
返回;
int n=c.getAndIncrement();
添加(n);
}捕获(例外e){
e、 printStackTrace();
}
}
最后{
锁定。解锁(id);
}
}
}
}
公共级Tmp{
公共静态void main(字符串[]args)引发IOException{
计数器c=新计数器(1);
线程[]t=新线程[2];
列表值=新的ArrayList();
彼得森l=新彼得森();
对于(int i=0;i

尽管我期望
System.out.println(values.size())
打印
20000
它在每次运行时打印不同的数字。为什么会这样?我做错了什么?

我想,这是因为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,而不是数组内容。

解锁时不创建内存屏障,以保证之前发生的情况

添加一个
volatile boolean barr
并在unlock中将其写入true,并将锁中的while更改为
while(barr&&flag[j]&&被害人==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
    }
}
for(int i=0;i
正如其他人所指出的,Java对数组元素没有易变的读/写操作


您可以使用
AtomicIntegerArray
,它具有
get()
set()
的易变效果

它不起作用,因为您使数组引用不稳定,而不是内容(如前所述)。但是你总是可以在一个类中包装一个易失性字段,并将其组成一个数组。

我不明白你的第一句话,你在lock类中的“lock”是什么意思不是原子的?关于第二个问题,有没有办法将数组元素标记为volatile?对于
volatile
2-booleans数组,用volatile int替换为4个可能的值怎么样?@victor这是不可接受的,因为如果线程0将位0设置为true,而线程1使用
flag=flag | 1store)01、10将位1设置为true会怎么样(两个都不正确)或11(正确)你需要一个原子比较和交换来用intI解决这个问题。我不明白你在
barr
中的意思,我应该在
unlock
中设置
barr=true
,那么在
lock
方法中呢?@mario你希望解锁之前的动作发生在成功锁定之后的动作之前,所以你只需要阅读
条r
处于锁定状态(仅在同一字段上保证使用
volatile
s之前发生)