Java 在多线程环境中交换变量

Java 在多线程环境中交换变量,java,Java,最近有人问了我一个让我难堪的问题 public void swapEngine(Car a, Car b) { Engine temp = a.engine; a.engine = b.engine; b.engine = temp; } 这不是线程安全的方法。如果线程1调用swapEngine(car1,car2),然后线程2调用swapEngine(car1,car3),则car2可能以car3的引擎结束。解决此问题最明显的方法是同步方法 同步该方法可能会导致效率低下。如果线程

最近有人问了我一个让我难堪的问题

public void swapEngine(Car a, Car b) {
  Engine temp = a.engine;
  a.engine = b.engine;
  b.engine = temp;
}
这不是线程安全的方法。如果线程1调用
swapEngine(car1,car2)
,然后线程2调用
swapEngine(car1,car3)
,则
car2
可能以
car3
的引擎结束。解决此问题最明显的方法是同步方法

同步该方法可能会导致效率低下。如果线程1调用
swapEngine(car1,car2)
,线程2调用
swapEngine(car3,car4)
,该怎么办?这两条线决不能互相干扰。在这种情况下,理想的情况是两个线程并行交换引擎。同步该方法可防止发生这种情况

是否有另一种技术可以在仍然利用并行性的情况下以线程安全的方式交换这些引擎


编辑:将方法公开。

正如评论所说,你可以自己锁车。然而,如果车辆不总是按相同的顺序锁定,这可能会导致死锁

因此,如果车辆具有唯一标识符,您只需对车辆进行排序,然后交换:

void swapEngine(Car a, Car b) {
    Comparator<Car> byId = Comparator.comparing(Car::id);
    Car[] cars = new Car[] {a, b};
    Arrays.sort(cars, byId);
    doSwap(cars[0]), cars[1];
}

private void doSwap(Car a, Car b) { 
    synchronized(a) {
        synchronized(b) {
            Engine temp = a.engine;
            a.engine = b.engine;
            b.engine = temp;
        }
    }
}
void swapEngine(a车、b车){
比较器byId=比较器。比较(Car::id);
Car[]cars=新车[]{a,b};
数组。排序(cars、byId);
doSwap(cars[0]),cars[1];
}
私家车(a车、b车){
已同步(a){
已同步(b){
发动机温度=a.发动机;
a、 发动机=b.发动机;
b、 发动机=温度;
}
}
}

如果汽车没有任何唯一的ID来进行比较,您可以根据其标识哈希代码对其进行排序(使用
System.identityHashCode(汽车)
)。这个哈希代码是独一无二的,除非你有巨大的内存、大量的汽车和厄运。如果您真的担心出现这种情况,那么Guava有一个您可以使用的工具。

如果您将
Car.engine
存储在中,您可以使用CAS操作交换它们:

public <T> void atomicSwap(AtomicReference<T> a, AtomicReference<T> b) {
    for(;;) {
        T aa = a.getAndSet(null);
        if (aa != null) {
            T bb = b.getAndSet(null);  
            if (bb != null) {
                // this piece will be reached ONLY if BOTH `a` and `b` 
                // contained non-null (and now contain null)
                a.set(bb);
                b.set(aa);
                return;
            } else {
                // if `b` contained null, try to restore old value of `a`
                // to avoid deadlocking
                a.compareAndSet(null, aa);
            }
        } 
    }          
}

嗯,您可以将Lock()添加到Car实例中。然后,您只能锁定那些参与交换的实例。@mkrakhin在
交换(a,b)中容易死锁;掉期(b、a)情况。@maba,在这种情况下,同一辆车不能在两个商店:)我认为该方法应该是汽车抽象本身的一部分,就像
void swapEngine(car otherCar){this.engine=otherCar.engine}
@Kayaman是的,但OP可以使用
tryLock()
。然后,他可以从swapEngine boolean返回,在尝试获取锁的过程中通知失败或成功,并将swapEngine()放入
while
循环中。
public <T> T getValue(AtomicReference<T> a) {
    for(;;) {
        T v = a.get();
        if (v != null)
            return v;
    }
}

public <T> T setValue(AtomicReference<T> a, T value) {
    for(;;) {
        T old = a.get();
        if (old != null && a.compareAndSet(old, value))
            return old; 
    }
}