我有办法以原子方式锁定2个或更多锁或监视器吗?我的意思是,假设我的线程希望锁定2个锁并等待它们都是空闲的,即从不锁定一个然后等待另一个?
答案 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_0
,l_1
,...,l_n
。要以原子方式获取所有锁,请引入额外的锁l_extra
:
synchronized (l_extra) {
synchronized (l_0) {
synchronized (l_1) {
// ...
}
}
}