4

我有办法自动锁定 2 个或更多锁或监视器吗?我的意思是,假设我的线程希望锁定 2 个锁并等待它们都空闲,即永远不要锁定一个然后等待另一个?

4

3 回答 3

0

我认为这相当于哲学家就餐问题 - 访问Wikipedia 条目可以为您提供几种可能的解决方案。

干杯,

于 2012-11-01T09:52:17.613 回答
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 担心这一点。只要您需要获取多个锁的所有代码都这样做,那么您将永远不会死锁。我包含了 test main 方法,因此您可以自己测试它。

锁总是以相同的顺序获取,除非锁链中的两个或多个身份哈希相同(非常罕见的事件),在这种情况下,首先获取该哈希唯一的决胜局以确保只有一个线程试图锁定具有该特定哈希码的多个对象可以继续。

应该很明显,这可以如何扩展到任何其他锁类型,但要小心重复的不可重入锁,因为这导致死锁。同步块是可重入的,因此这里没有风险。它也可以很容易地与 Callable 类型一起工作。

于 2013-06-10T03:16:08.830 回答
0

您可以使用额外的锁。

假设您想要获取锁l_0, l_1, ..., l_n。要以原子方式获取所有锁,请引入一个附加锁l_extra

synchronized (l_extra) {
   synchronized (l_0) {
      synchronized (l_1) {
         // ...
      }
   }
}
于 2013-06-10T18:59:59.613 回答