简体   繁体   中英

Java - Deadlock using SWAP function to solve mutual exclusion

I would like to solve mutual exclusion using the Swap function but the program suffer from deadlock and I don't know why. It seems the problem occur when a thread exec two consecutive times.

The swap lock logic:

Every thread make use of a local variable and a shared variable. A thread at first lock its local variable (eg assume 1 value). When a thread local variable is unlocked (eg assume 0 value) it can execute the critical section, if the thread local variable is locked (eg assume 1 value) the thread is in a busy-waiting (the busy waiting tests the local variable unlocking and calls the swap function)

The swap function sets the local variable to the shared variable value and viceversa.The swap function has to be ATOMIC.

When a thread call swap, if the the "shared" variables is 0 (unlock) after swap we have that shared variable is 1 and the local to 0. So only that thread can access to the critical section no others.

At the end (no more critical section) the thread unlock the shared variable.

Main

public class Mutex {

    public static void main(String []args){
        LockVar var = new LockVar(0);
        ThreadSwap th0 = new ThreadSwap(var);
        ThreadSwap th1 = new ThreadSwap(var);
        ThreadSwap th2 = new ThreadSwap(var);
        th0.start();
        th1.start();
        th2.start();
    }
}

Thread Class (The logic of this type of mutex is emphasized)

class ThreadSwap extends Thread{

    private LockVar shared_var;

    public ThreadSwap(LockVar var){
    this.shared_var = var;
    }
    @Override
    public void run(){
        LockVar local = new LockVar(1);   
        while(true){
       ---> local.setVar(1);
       ---> while(local.getVar() == 1){Synch.SWAP(shared_var, local);}
            System.out.println("Thread " + getId() + " exec critical section."); 
            // Critical section
            System.out.println("Thread " + getId() + " is leaving critical section.");
       ---> shared_var.setVar(0);
        }
    }
}

Swap function

class Synch{
public static synchronized void SWAP(LockVar shared, LockVar local){
    int temp = shared.getVar();
    shared.setVar(local.getVar());
    local.setVar(temp);
}
...
}

Shared var Class

class LockVar{

    private volatile int var;

    public LockVar(int value){
        this.var = value;
    }
    public int getVar(){
        return this.var;
    }
    public void setVar(int value){
        this.var=value;
    }
}

Imagine this scenario:

  1. The first thread swaps local and shared_var. Now shared_var is 1 and local is 0.
  2. Since the synchronization on SWAP is on the Synch class, as soon as the first thread is done with SWAP, Thread 2 can get into it. Thread 2 makes temp = 1, and at that point the monitor switches back to Thread 1.
  3. Thread 1 finishes the first iteration of the loop, printing the messages of exec and leaving critical section and setting shared_var = 0. Monitor moves to Thread 2.
  4. Thread 2 proceeds with SWAP. where it left it. It executes shared.setVar(local.getVar()); (now shared_var == 1) and local.setVar(temp); (remember temp is 1 from step 2).

The deadlock has been produced: shared_var is 1 and no thread is in the critical section. This is because SWAP is not atomic (the monitor can move away from a thread executing a synchronized method, it will just not allow other threads to enter such method nor others method synchronized on the same lock (the class in the case of a static method, the instance of the object in the case of a non-static method)).

In order to fix this you should not allow shared to change while SWAP is executing, by finding a way of making it atomic. A possibility would be to get rid of local and use compareAndSet on an AtomicBoolean which would play the role of shared_var at the moment.

Atomic types guarantee atomicity and volatility (see documentation ). This way, every thread should get a reference (excuse my loose nomenclature) to the AtomicBoolean used for signaling and use compareAndSet to atomically update the value of the variable in a way that is visible immediately to the other threads:

import java.util.concurrent.atomic.AtomicBoolean;

public class Mutex {

  public static void main(String []args){
    AtomicBoolean isLocked = new AtomicBoolean(false);
    ThreadSwap th0 = new ThreadSwap(isLocked);
    ThreadSwap th1 = new ThreadSwap(isLocked);
    ThreadSwap th2 = new ThreadSwap(isLocked);
    th0.start();
    th1.start();
    th2.start();
  }
}

class ThreadSwap extends Thread {

  private AtomicBoolean shared_IsLocked;

  public ThreadSwap(AtomicBoolean var){
    this.shared_IsLocked = var;
  }

  @Override
  public void run(){
    while(true){
      // While the flag is true (locked), keep checking
      // If it is false (not locked), atomically change its value and keep going
      while(!shared_IsLocked.compareAndSet(false, true));
      System.out.println("Thread " + getId() + " exec critical section."); 
            // Critical section
      System.out.println("Thread " + getId() + " is leaving critical section.");
      shared_IsLocked.set(false);
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM