2011-08-23 38 views
3

我试图用Java实现Peterson's algorithm,并创造了暂时实施彼得森锁在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()); 
    } 
} 

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

回答

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 
    } 
} 
+0

我不明白你的意思'barr'什么,我应该设置'unlock'中的'barr = true',在'lock'方法中怎么样? –

+0

@mario你想行动*发生之前解锁之前*之后成功锁定一个动作,所以你只需要在锁是读了'barr'(将*用'volatile's只能保证在之前*发生相同的字段) –

+0

由于'joing',我似乎得到了错误的结果。谢谢 :) –

0

我想,这是因为Lock类中的lock()方法不是原子的。具体地说,该码修改存储器地址,然后使用它们两者的w/o锁定检查条件:

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,而不是数组内容。

+0

我不明白你的第一句话,你的意思是在锁类“锁定”不是原子?关于第二个,有没有办法将数组元素标记为volatile? –

+0

@Mario更新了我的回答。 –

+0

对于'volatile' 2-布尔阵列,关于什么用挥发性INT与4个可能的值代吗? –

0

正如其他人所指出的,Java没有挥发性的读/数组元素写。

您可以使用AtomicIntegerArray其中get()set()具有不稳定的影响。

-2

,因为你所做的数组引用挥发,而不是内容(如已经指出的那样),这是行不通的。但是你总是可以在一个类中包装一个易变的字段并创建一个这样的数组。