Burns和Lynch在Java中实现互斥算法:是否会因为指令重新排序而出现问题?

Burns和Lynch在Java中实现互斥算法:是否会因为指令重新排序而出现问题?,java,multithreading,algorithm,mutual-exclusion,Java,Multithreading,Algorithm,Mutual Exclusion,我在下面的文章第4页(836)找到了一个相当简单的n进程互斥算法: 我喜欢它,因为它的内存使用量最小,goto应该允许我在方法enterCriticalRegion()中实现它,该方法返回一个布尔值,指示进程是否成功获取锁(即到达第8行),或者它是否命中一个goto,需要稍后重试,而不是忙于等待。(公平和饥饿对我来说并不是一个真正的问题) 我尝试在Java中实现这一点,并通过让一组线程尝试快速连续地进入关键区域(看起来很长,但大部分是注释)来进行测试: 导入java.util.concurren

我在下面的文章第4页(836)找到了一个相当简单的n进程互斥算法:

我喜欢它,因为它的内存使用量最小,goto应该允许我在方法
enterCriticalRegion()
中实现它,该方法返回一个布尔值,指示进程是否成功获取锁(即到达第8行),或者它是否命中一个goto,需要稍后重试,而不是忙于等待。(公平和饥饿对我来说并不是一个真正的问题)

我尝试在Java中实现这一点,并通过让一组线程尝试快速连续地进入关键区域(看起来很长,但大部分是注释)来进行测试:

导入java.util.concurrent.AtomicInteger;
公共级中小企业{
//关键部分中用于计数过程的变量(用于验证)
私有静态AtomicInteger criticalCount=新的AtomicInteger(0);
//共享变量F:flag的数组[1..N];
私有静态最终布尔值[]F=新布尔值[10000];
//一些过程局部变量
私有final int processID;
私有布尔atLine7;
公共BurnsME(int processID){
this.processID=processID;
this.atLine7=false;
}
/**
*尝试进入关键区域。
* 
*@return T-成功;F-失败,需要稍后再试
*/
公共布尔值enterCriticalRegion(){
//伯恩斯-林奇算法
//使用不可分割读写的互斥,第836页
如果(!atLine7){
//3:F[i]向下
F[processID]=false;
//4:对于j:=1到i-1,如果F[j]=up goto 3

对于java内存模型中的(int-process=0;process,您不能保证对F[i]的写入在以后被另一个线程读取时是可见的

这种可见性问题的标准解决方案是将共享变量声明为volatile,但在这种情况下,F是一个数组,对F[i]的写入/读取不会更改F的值


不可能声明“volatiles数组…”,但可以将F声明为AtomicIntegerArray,并使用它以原子方式更改数组内容,而不必担心线程可见性。

在java内存模型中,不能保证写入F[i]稍后将对另一个读取它的线程可见。请阅读volatile关键字以及为什么不能将其用于数组。解决方案是,如果替换
boolean[]
使用
AtomicIntegerArray
并使用其
set
get
方法,一切工作都像一个符咒。似乎这就是问题所在。谢谢。
program Process_i;
    type flag = (down, up);
    shared var F : array [1..N] of flag;
    var j : 1..N;
begin
    while true do begin
        1: F[i] := down;
        2: remainder;     (* remainder region *)
        3: F[i] := down;
        4: for j := 1 to i-1 do
               if F[j] = up then goto 3;
        5: F[i] := up;
        6: for j := 1 to i-1 do
               if F[j] = up then goto 3;
        7: for j := i+1 to N do
               if F[j] = up then goto 7;
        8: critical;      (* critical region *)
    end
end.
import java.util.concurrent.atomic.AtomicInteger;

public class BurnsME {

    // Variable to count processes in critical section (for verification)
    private static AtomicInteger criticalCount = new AtomicInteger(0);

    // shared var F : array [1..N] of flag;
    private static final boolean[] F = new boolean[10000];

    // Some process-local variables
    private final int processID;
    private boolean   atLine7;

    public BurnsME(int processID) {
        this.processID = processID;
        this.atLine7   = false;
    }

    /**
     * Try to enter critical region.
     * 
     * @return T - success; F - failure, need to try again later 
     */
    public boolean enterCriticalRegion() {

        // Burns Lynch Algorithm
        // Mutual Exclusion Using Indivisible Reads and Writes, p. 836
        if (!atLine7) {
            // 3: F[i] down
            F[processID] = false;

            // 4: for j:=1 to i-1 do if F[j] = up goto 3
            for (int process=0; process<processID; process++)
                if (F[process]) return false;

            // 5: F[i] = up
            F[processID] = true;

            // 6: for j:=1 to i-1 do if F[j] = up goto 3
            for (int process=0; process<processID; process++)
                if (F[process]) return false;

            atLine7 = true;
        }

        // 7: for j:=i+1 to N do if F[j] = up goto 7
        for (int process=processID+1; process<F.length; process++) 
            if (F[process]) return false;

        // Verify mutual exclusion
        if (criticalCount.incrementAndGet()>1) {
            System.err.println("TWO PROCESSES ENTERED CRITICAL SECTION!");
            System.exit(1);
        }

        // 8: critical region
        return true;
    }

    /**
     * Leave critical region and allow next process in
     */
    public void leaveCriticalRegion() {

        // Reset state
        atLine7 = false;
        criticalCount.decrementAndGet();

        // Release critical region lock
        // 1: F[i] = down
        F[processID] = false;
    }

    //===============================================================================
    // Test Code

    private static final int THREADS = 50;

    public static void main(String[] args) {

        System.out.println("Launching "+THREADS+" threads...");

        for (int i=0; i<THREADS; i++) {
            final int threadID = i;

            new Thread() {
                @Override
                public void run() {
                    BurnsME mutex = new BurnsME(threadID);

                    while (true) {
                        if (mutex.enterCriticalRegion()) {
                            System.out.println(threadID+" in critical region");
                            mutex.leaveCriticalRegion();
                        }
                    }
                }
            }.start();
        }

        while (true);
    }
}