我有办法原子地锁定2个或更多的锁或监视器吗?

时间:2012-11-01 09:46:27

标签: java concurrency locking critical-section

我有办法以原子方式锁定2个或更多锁或监视器吗?我的意思是,假设我的线程希望锁定2个锁并等待它们都是空闲的,即从不锁定一个然后等待另一个?

3 个答案:

答案 0 :(得分:0)

我认为这相当于餐饮哲学家的问题 - 访问Wikipedia entry可以指出几种可能的解决方案。

干杯,

答案 1 :(得分:0)

无法以原子方式获取多个监视器。

锁定系统可以设计为做类似的事情,尽管效率低下。

如果没有关于您的用例的更多信息,很难规定任何可能的解决方案,但有一种模式允许安全获取多个锁,而不会出现死锁。

我知道这不是你要求的,我很乐意指出你有更多细节的更好的方向,但如果你唯一的愿望是避免死锁,那就这样做:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LockSort{

  private int ptr = 0;
  private final Object[] locks;
  private final Runnable toRun;

  private LockSort(Runnable r, Object[] l){
    locks = l;
    Arrays.sort(locks, identCompare);
    toRun = r;
  }

  private static final Comparator<Object> identCompare = new Comparator<Object>(){
    @Override
    public int compare(Object a, Object b){
      return System.identityHashCode(a) - System.identityHashCode(b);
    }
  };

  private void lockSingle(){
    synchronized(locks[ptr++]){
      dispatch();
    }
  }

  private static final Map<Integer, MutableInteger> breakers = new HashMap<>();

  private static MutableInteger getTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(null != b){
        b.val++;
        return b;
      }
      breakers.put(hash, b = new MutableInteger(1));
      return b;
    }
  }

  private static void releaseTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(0 == --b.val)
        breakers.remove(hash);
    }
  }

  private void breakTie(){
    final Integer hash = System.identityHashCode(locks[ptr]);
    try{
      synchronized(getTieBreaker(hash)){
        synchronized(locks[ptr++]){
          dispatch();
        }
      }
    }finally{
      releaseTieBreaker(hash);
    }
  }

  private void dispatch(){
    if(ptr == locks.length)
      toRun.run();
    else if(ptr + 1 == locks.length || System.identityHashCode(locks[ptr]) != System.identityHashCode(locks[ptr + 1]))
      lockSingle();
    else
      breakTie();
  }    import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LockSort{

  private int ptr = 0;
  private final Object[] locks;
  private final Runnable toRun;

  private LockSort(Runnable r, Object[] l){
    locks = l;
    Arrays.sort(locks, identCompare);
    toRun = r;
  }

  private static final Comparator<Object> identCompare = new Comparator<Object>(){
    @Override    import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LockSort{

  private int ptr = 0;
  private final Object[] locks;
  private final Runnable toRun;

  private LockSort(Runnable r, Object[] l){
    locks = l;
    Arrays.sort(locks, identCompare);
    toRun = r;
  }

  private static final Comparator<Object> identCompare = new Comparator<Object>(){
    @Override
    public int compare(Object a, Object b){
      return System.identityHashCode(a) - System.identityHashCode(b);
    }
  };

  private void lockSingle(){
    synchronized(locks[ptr++]){
      dispatch();
    }
  }

  private static final Map<Integer, MutableInteger> breakers = new HashMap<>();

  private static MutableInteger getTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(null != b){
        b.val++;
        return b;
      }
      breakers.put(hash, b = new MutableInteger(1));
      return b;
    }
  }

  private static void releaseTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(0 == --b.val)
        breakers.remove(hash);
    }
  }

  private void breakTie(){
    final Integer hash = System.identityHashCode(locks[ptr]);
    try{
      synchronized(getTieBreaker(hash)){
        synchronized(locks[ptr++]){
          dispatch();
        }
      }
    }finally{
      releaseTieBreaker(hash);
    }
  }

  private void dispatch(){
    if(ptr == locks.length)
      toRun.run();
    else if(ptr + 1 == locks.length || System.identityHashCode(locks[ptr]) != System.identityHashCode(locks[ptr + 1]))
      lockSingle();
    else
      breakTie();
  }

  public static void lockMultipleAndRun(Runnable toRun, Object... toLock){
    new LockSort(toRun, toLock).dispatch();
  }

  public static void lockMultipleAndRun(Runnable toRun, Collection<Object> toLock){
    new LockSort(toRun, toLock.toArray()).dispatch();
  }

  private static class MutableInteger{
    int val;

    MutableInteger(int i){
      val = i;
    }
  }

  public static void main(String args[]){
    final int THREADS = 0 == args.length ? Runtime.getRuntime().availableProcessors() : Integer.valueOf(args[0]);

    for(int j = 0; j < 1000; j++){
      final int RUNID = j;
      final Object locks[] = new Object[300];
      for(int i = 0; i < 300; i++){
        locks[i] = new Object(){
        };
      }

      List<Thread> threads = new ArrayList<>(50);
      for(int i = 0; i < THREADS; i++){

        final int ID = i;
        Thread t = new Thread(new Runnable(){
          @Override
          public void run(){
            for(int i = 0; i < 1000; i++){
              int a = (int) Math.floor(Math.random() * 300.0);
              int b = (int) Math.floor(Math.random() * 300.0);
              final int l = Math.min(a, b);
              final int h = Math.max(a, b);

              final int CT = i;

              lockMultipleAndRun(new Runnable(){
                @Override
                public void run(){
                  System.out.println(RUNID + ":" + ID + " @ " + CT + " w/ " + l + "-" + h);
                }
              }, Arrays.copyOfRange(locks, l, h));

            }
          }
        });
        t.start();
        threads.add(t);
      }

      for(Thread t : threads){
        try{
          t.join();
        }catch(InterruptedException e){
          throw new RuntimeException(e);
        }
      }

    }
  }

}

    public int compare(Object a, Object b){
      return System.identityHashCode(a) - System.identityHashCode(b);
    }
  };

  private void lockSingle(){
    synchronized(locks[ptr++]){
      dispatch();
    }
  }

  private static final Map<Integer, MutableInteger> breakers = new HashMap<>();

  private static MutableInteger getTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(null != b){
        b.val++;
        return b;
      }
      breakers.put(hash, b = new MutableInteger(1));
      return b;
    }
  }

  private static void releaseTieBreaker(Integer hash){
    synchronized(breakers){
      MutableInteger b = breakers.get(hash);
      if(0 == --b.val)
        breakers.remove(hash);
    }
  }

  private void breakTie(){
    final Integer hash = System.identityHashCode(locks[ptr]);
    try{
      synchronized(getTieBreaker(hash)){
        synchronized(locks[ptr++]){
          dispatch();
        }
      }
    }finally{
      releaseTieBreaker(hash);
    }
  }

  private void dispatch(){
    if(ptr == locks.length)
      toRun.run();
    else if(ptr + 1 == locks.length || System.identityHashCode(locks[ptr]) != System.identityHashCode(locks[ptr + 1]))
      lockSingle();
    else
      breakTie();
  }

  public static void lockMultipleAndRun(Runnable toRun, Object... toLock){
    new LockSort(toRun, toLock).dispatch();
  }

  public static void lockMultipleAndRun(Runnable toRun, Collection<Object> toLock){
    new LockSort(toRun, toLock.toArray()).dispatch();
  }

  private static class MutableInteger{
    int val;

    MutableInteger(int i){
      val = i;
    }
  }

  public static void main(String args[]){
    final int THREADS = 0 == args.length ? Runtime.getRuntime().availableProcessors() : Integer.valueOf(args[0]);

    for(int j = 0; j < 1000; j++){
      final int RUNID = j;
      final Object locks[] = new Object[300];
      for(int i = 0; i < 300; i++){
        locks[i] = new Object(){
        };
      }

      List<Thread> threads = new ArrayList<>(50);
      for(int i = 0; i < THREADS; i++){

        final int ID = i;
        Thread t = new Thread(new Runnable(){
          @Override
          public void run(){
            for(int i = 0; i < 1000; i++){
              int a = (int) Math.floor(Math.random() * 300.0);
              int b = (int) Math.floor(Math.random() * 300.0);
              final int l = Math.min(a, b);
              final int h = Math.max(a, b);

              final int CT = i;

              lockMultipleAndRun(new Runnable(){
                @Override
                public void run(){
                  System.out.println(RUNID + ":" + ID + " @ " + CT + " w/ " + l + "-" + h);
                }
              }, Arrays.copyOfRange(locks, l, h));

            }
          }
        });
        t.start();
        threads.add(t);
      }

      for(Thread t : threads){
        try{
          t.join();
        }catch(InterruptedException e){
          throw new RuntimeException(e);
        }
      }

    }
  }

}


  public static void lockMultipleAndRun(Runnable toRun, Object... toLock){
    new LockSort(toRun, toLock).dispatch();
  }

  public static void lockMultipleAndRun(Runnable toRun, Collection<Object> toLock){
    new LockSort(toRun, toLock.toArray()).dispatch();
  }

  private static class MutableInteger{
    int val;

    MutableInteger(int i){
      val = i;
    }
  }

  public static void main(String args[]){
    final int THREADS = 0 == args.length ? Runtime.getRuntime().availableProcessors() : Integer.valueOf(args[0]);

    for(int j = 0; j < 1000; j++){
      final int RUNID = j;
      final Object locks[] = new Object[300];
      for(int i = 0; i < 300; i++){
        locks[i] = new Object(){
        };
      }

      List<Thread> threads = new ArrayList<>(50);
      for(int i = 0; i < THREADS; i++){

        // Shuffle the locks per-thread to see more highly chaotic and difficult to deal with locking behavior
        final Object[] myLocks = new Object[300];
        System.arraycopy(locks, 0, myLocks, 0, 300);
        for(int k = 0; k < 300; k++){
          int a = (int) Math.floor(Math.random() * 300.0);
          int b = (int) Math.floor(Math.random() * 300.0);
          Object o = myLocks[a];
          myLocks[a] = myLocks[b];
          myLocks[b] = o;
        }

        final int ID = i;
        Thread t = new Thread(new Runnable(){
          @Override
          public void run(){
            for(int i = 0; i < 1000; i++){
              int a = (int) Math.floor(Math.random() * 300.0);
              int b = (int) Math.floor(Math.random() * 300.0);
              final int l = Math.min(a, b);
              final int h = Math.max(a, b);

              final int CT = i;

              lockMultipleAndRun(new Runnable(){
                @Override
                public void run(){
                  System.out.println(RUNID + ":" + ID + " @ " + CT + " w/ " + l + "-" + h);
                }
              }, Arrays.copyOfRange(locks, l, h));

            }
          }
        });
        t.start();
        threads.add(t);
      }

      for(Thread t : threads){
        try{
          t.join();
        }catch(InterruptedException e){
          throw new RuntimeException(e);
        }
      }

    }
  }

}

它看起来像很多递归调用,但让JIT担心这一点。只要所有需要获取多个锁的代码执行此操作,您就不会陷入僵局。我包含了测试主要方法,因此您可以自己测试。

锁总是以相同的顺序获取,除非锁链中的两个或多个标识哈希是相同的(一个极其罕见的事件),在这种情况下,首先获取该哈希特有的tie tie以确保只有一个线程尝试使用该特定哈希码锁定多个对象可以继续。

应该明白如何将其扩展到任何其他锁定类型,但要注意重复的非重入锁定,因为导致死锁。同步块是可重入的,因此这里没有风险。它也可以很容易地用Callable类型工作。

答案 2 :(得分:0)

您可以使用额外的锁定。

假设你想获得锁l_0l_1,...,l_n。要以原子方式获取所有锁,请引入额外的锁l_extra

synchronized (l_extra) {
   synchronized (l_0) {
      synchronized (l_1) {
         // ...
      }
   }
}