何时在 Java 中使用 AtomicReference?

发布于 2024-09-28 04:55:21 字数 252 浏览 3 评论 0原文

我们什么时候使用 AtomicReference

所有多线程程序都需要创建对象吗?

提供一个应该使用 AtomicReference 的简单示例。

When do we use AtomicReference?

Is it needed to create objects in all multithreaded programs?

Provide a simple example where AtomicReference should be used.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

梦晓ヶ微光ヅ倾城 2024-10-05 04:55:21

原子引用应该用在需要执行简单原子的设置中(即<对引用的线程安全(em>线程安全,非平凡)操作,基于监视器的同步对此不合适。假设您只想在处理过程中对象状态发生更改时设置特定字段:

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,newValue);

由于原子引用语义,即使 cache 对象在线程之间共享,您也可以执行此操作,而无需使用已同步。一般来说,除非您知道自己在做什么,否则最好使用同步器或 java.util.concurrent 框架,而不是裸露的 Atomic*

两篇优秀的死树参考资料将向您介绍这个主题:

请注意(我不知道这是否一直如此)引用赋值(即=)本身就是原子的(更新原始 64位类型,例如longdouble 可能不是原子的;但更新引用始终是原子的,即使它是 64 位)而不显式使用 原子*
请参阅 Java 语言规范 3ed,第 17.7 节

Atomic reference should be used in a setting where you need to do simple atomic (i.e. thread-safe, non-trivial) operations on a reference, for which monitor-based synchronization is not appropriate. Suppose you want to set a specific field only if the state of the object has changed during processing:

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,newValue);

Because of the atomic reference semantics, you can do this even if the cache object is shared amongst threads, without using synchronized. In general, you're better off using synchronizers or the java.util.concurrent framework rather than bare Atomic* unless you know what you're doing.

Two excellent dead-tree references which will introduce you to this topic:

Note that (I don't know if this has always been true) reference assignment (i.e. =) is itself atomic (updating primitive 64-bit types like long or double may not be atomic; but updating a reference is always atomic, even if it's 64 bit) without explicitly using an Atomic*.
See the Java Language Specification 3ed, Section 17.7.

不及他 2024-10-05 04:55:21

当您需要通过将多个线程访问的内容(在不可变对象中)替换为这些线程之间共享的新副本(不可变对象)来更新它时,原子引用是理想的选择。这是一个非常密集的陈述,所以我将对其进行一些分解。

首先,不可变对象是在构造后实际上不会改变的对象。通常,不可变对象的方法会返回同一类的新实例。一些示例包括包装类 LongDouble,以及 String,仅举几例。 (根据 JVM 上的编程并发,不可变对象是现代并发的关键部分。)

接下来,为什么 AtomicReference易失性 更好> 共享该共享价值的对象?一个简单的代码示例将显示差异。

volatile String sharedValue;

static final Object lock = new Object();

void modifyString() {
    synchronized (lock) {
        sharedValue = sharedValue + "something to add";
    }
}

每次想要根据当前值修改该易失性字段引用的字符串时,首先需要获得该对象的锁。这可以防止其他线程在此期间进入并更改新字符串连接中间的值。然后,当您的线程恢复时,您会破坏另一个线程的工作。但老实说,该代码可以工作,看起来很干净,并且会让大多数人感到高兴。

有点问题。它很慢。特别是当该锁对象存在大量争用时。那是因为大多数锁都需要操作系统系统调用,并且您的线程将被阻塞并被上下文切换出 CPU,以便为其他进程让路。

另一种选择是使用 AtomicReference。

public static AtomicReference<String> shared = new AtomicReference<>();
String init = "Inital Value";
shared.set(init);
//now we will modify that value
boolean success = false;
while (!success) {
    String prevValue = shared.get();
    // do all the work you need to
    String newValue = shared.get() + "let's add something";
    // Compare and set
    success = shared.compareAndSet(prevValue, newValue);
}

现在为什么这样更好呢?老实说,代码比以前稍微不那么干净了。但在 AtomicRefrence 的幕后发生了一些非常重要的事情,那就是比较和交换。
导致切换发生的是单个 CPU 指令,而不是操作系统调用。这是CPU 上的一条指令。而且因为没有锁,所以在行使锁的情况下没有上下文切换,这节省了更多时间!

问题是,对于 AtomicReferences,这不使用 .equals() 调用,而是使用 == 比较预期值。因此,请确保预期的是循环中 get 返回的实际对象。

An atomic reference is ideal to use when you need to update content (in an immutable object) accessed by multiple threads by replacing it with a new copy (of the immutable object) shared between these threads. That is a super dense statement, so I will break it down a bit.

First, an immutable object is an object that is effectively not changed after construction. Frequently, an immutable object's methods return new instances of that same class. Some examples include the wrapper classes Long and Double, as well as String, just to name a few. (According to Programming Concurrency on the JVM, immutable objects are a critical part of modern concurrency.)

Next, why is an AtomicReference better than a volatile object for sharing that shared value? A simple code example will show the difference.

volatile String sharedValue;

static final Object lock = new Object();

void modifyString() {
    synchronized (lock) {
        sharedValue = sharedValue + "something to add";
    }
}

Every time you want to modify the string referenced by that volatile field based on its current value, you first need to obtain a lock on that object. This prevents some other thread from coming in during the meantime and changing the value in the middle of the new string concatenation. Then when your thread resumes, you clobber the work of the other thread. But honestly that code will work, it looks clean, and it would make most people happy.

Slight problem. It is slow. Especially if there is a lot of contention of that lock Object. Thats because most locks require an OS system call, and your thread will block and be context switched out of the CPU to make way for other processes.

The other option is to use an AtomicReference.

public static AtomicReference<String> shared = new AtomicReference<>();
String init = "Inital Value";
shared.set(init);
//now we will modify that value
boolean success = false;
while (!success) {
    String prevValue = shared.get();
    // do all the work you need to
    String newValue = shared.get() + "let's add something";
    // Compare and set
    success = shared.compareAndSet(prevValue, newValue);
}

Now why is this better? Honestly that code is a little less clean than before. But there is something really important that happens under the hood in AtomicRefrence, and that is compare and swap.
It is a single CPU instruction, not an OS call, that makes the switch happen. That is a single instruction on the CPU. And because there are no locks, there is no context switch in the case where the lock gets exercised which saves even more time!

The catch is, for AtomicReferences, this does not use a .equals() call, but instead an == comparison for the expected value. So make sure the expected is the actual object returned from get in the loop.

拥抱影子 2024-10-05 04:55:21

以下是 AtomicReference 的一个用例:

考虑充当数字范围的此类,并使用单独的 AtmomicInteger 变量来维护数字下限和上限。

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

setLower 和 setUpper 都是先检查后执行的序列,但它们没有使用足够的锁定来使它们成为原子的。如果数字范围为 (0, 10),并且一个线程调用 setLower(5),而另一个线程调用 setUpper(4),那么在某些不幸的时机,两个线程都将通过 setter 中的检查,并且两个修改都将被应用。结果是范围现在保持 (5, 4) 无效状态。因此,虽然底层 AtomicIntegers 是线程安全的,但复合类却不是。这可以通过使用 AtomicReference 而不是使用单独的 AtomicIntegers 作为上限和下限来解决。

public class CasNumberRange {
    // Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;

        private IntPair(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }
    }

    private final AtomicReference<IntPair> values = 
            new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() {
        return values.get().lower;
    }

    public void setLower(int lower) {
        while (true) {
            IntPair oldv = values.get();
            if (lower > oldv.upper)
                throw new IllegalArgumentException(
                    "Can't set lower to " + lower + " > upper");
            IntPair newv = new IntPair(lower, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }

    public int getUpper() {
        return values.get().upper;
    }

    public void setUpper(int upper) {
        while (true) {
            IntPair oldv = values.get();
            if (upper < oldv.lower)
                throw new IllegalArgumentException(
                    "Can't set upper to " + upper + " < lower");
            IntPair newv = new IntPair(oldv.lower, upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
}

Here is a use case for AtomicReference:

Consider this class that acts as a number range, and uses individual AtmomicInteger variables to maintain lower and upper number bounds.

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

Both setLower and setUpper are check-then-act sequences, but they do not use sufficient locking to make them atomic. If the number range holds (0, 10), and one thread calls setLower(5) while another thread calls setUpper(4), with some unlucky timing both will pass the checks in the setters and both modifications will be applied. The result is that the range now holds (5, 4)an invalid state. So while the underlying AtomicIntegers are thread-safe, the composite class is not. This can be fixed by using a AtomicReference instead of using individual AtomicIntegers for upper and lower bounds.

public class CasNumberRange {
    // Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;

        private IntPair(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }
    }

    private final AtomicReference<IntPair> values = 
            new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() {
        return values.get().lower;
    }

    public void setLower(int lower) {
        while (true) {
            IntPair oldv = values.get();
            if (lower > oldv.upper)
                throw new IllegalArgumentException(
                    "Can't set lower to " + lower + " > upper");
            IntPair newv = new IntPair(lower, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }

    public int getUpper() {
        return values.get().upper;
    }

    public void setUpper(int upper) {
        while (true) {
            IntPair oldv = values.get();
            if (upper < oldv.lower)
                throw new IllegalArgumentException(
                    "Can't set upper to " + upper + " < lower");
            IntPair newv = new IntPair(oldv.lower, upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
}
拿命拼未来 2024-10-05 04:55:21

应用乐观锁时可以使用 AtomicReference。您有一个共享对象,并且想要从多个线程更改它。

  1. 您可以创建共享对象的副本
  2. 修改共享对象
  3. 您需要检查共享对象是否仍然与以前相同 - 如果是,则使用修改后的副本的引用进行更新。

因为其他线程可能已经修改了它并且/可以在这两个步骤之间进行修改。您需要在原子操作中完成它。这就是 AtomicReference 可以提供帮助的地方

You can use AtomicReference when applying optimistic locks. You have a shared object and you want to change it from more than 1 thread.

  1. You can create a copy of the shared object
  2. Modify the shared object
  3. You need to check that the shared object is still the same as before - if yes, then update with the reference of the modified copy.

As other thread might have modified it and/can modify between these 2 steps. You need to do it in an atomic operation. this is where AtomicReference can help

青丝拂面 2024-10-05 04:55:21

这是一个非常简单的用例,与线程安全无关。

要在 lambda 调用之间共享对象,AtomicReference 是一个选项

public void doSomethingUsingLambdas() {

    AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();

    soSomethingThatTakesALambda(() -> {
        yourObjectRef.set(youObject);
    });

    soSomethingElseThatTakesALambda(() -> {
        YourObject yourObject = yourObjectRef.get();
    });
}

我并不是说这是好的设计或任何东西(这只是一个简单的示例),但是如果您在某些情况下,您需要在 lambda 调用之间共享对象,AtomicReference 是一个选项。

事实上,您可以使用任何持有引用的对象,甚至是只有一项的 Collection。然而,AtomicReference 是一个完美的选择。

Here's a very simple use case and has nothing to do with thread safety.

To share an object between lambda invocations, the AtomicReference is an option:

public void doSomethingUsingLambdas() {

    AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();

    soSomethingThatTakesALambda(() -> {
        yourObjectRef.set(youObject);
    });

    soSomethingElseThatTakesALambda(() -> {
        YourObject yourObject = yourObjectRef.get();
    });
}

I'm not saying this is good design or anything (it's just a trivial example), but if you have have the case where you need to share an object between lambda invocations, the AtomicReference is an option.

In fact you can use any object that holds a reference, even a Collection that has only one item. However, the AtomicReference is a perfect fit.

记忆之渊 2024-10-05 04:55:21

我们什么时候使用 AtomicReference?

AtomicReference是一种无需使用同步即可自动更新变量值的灵活方法。它支持单变量的无锁线程安全编程。

有多种方法可以实现线程安全并发 API。原子变量就是其中之一。

Lock 对象支持锁定习惯用法,可简化许多并发应用程序。

Executors 定义用于启动和管理线程的高级 API。

并发集合使管理大量数据变得更加容易,并且可以大大减少同步的需要。

原子变量具有最小化同步并有助于避免内存一致性错误的功能。

提供一个应使用 AtomicReference 的简单示例。

使用 AtomicReference 的示例代码:

String name1 = "Ravindra";

AtomicReference<String> reference =
    new AtomicReference<String>(name1 );

String name2 = "Ravindra Babu";
boolean result = reference.compareAndSet(name1 , name2 );
System.out.println("compareAndSet result: " + result );

是否需要在所有多线程程序中创建对象?

您不需要不必在所有多线程程序中使用AtomicReference

如果您想保护单个变量,请使用AtomicReference。如果您想保护代码块,请使用其他构造,例如 Lock /synchronized 等。

来源:docs.oracle.com

When do we use AtomicReference?

AtomicReference is flexible way to update the variable value atomically without use of synchronization. It supports lock-free thread-safe programming on single variables.

There are multiple ways of achieving Thread safety concurrent API. Atomic variables is one of them.

Lock objects support locking idioms that simplify many concurrent applications.

Executors define a high-level API for launching and managing threads.

Concurrent collections make it easier to manage large collections of data, and can greatly reduce the need for synchronization.

Atomic variables have features that minimize synchronization and help avoid memory consistency errors.

Provide a simple example where AtomicReference should be used.

Sample code with AtomicReference:

String name1 = "Ravindra";

AtomicReference<String> reference =
    new AtomicReference<String>(name1 );

String name2 = "Ravindra Babu";
boolean result = reference.compareAndSet(name1 , name2 );
System.out.println("compareAndSet result: " + result );

Is it needed to create objects in all multithreaded programs?

You don't have to use AtomicReference in all multi threaded programs.

If you want to guard a single variable, use AtomicReference. If you want to guard a code block, use other constructs like Lock /synchronized etc.

Source: docs.oracle.com

盛夏已如深秋| 2024-10-05 04:55:21

我就不多说了。我尊敬的朋友们已经提供了宝贵的意见。 本博客最后的完整运行代码应该可以消除任何混乱。这是一个多线程场景下的电影订座小程序。

一些重要的基本事实如下。
1>不同的线程只能竞争堆空间中的实例和静态成员变量。
2>易失性读取或写入完全是原子的,并且序列化/发生在内存之前并且仅从内存中完成。我这么说的意思是任何读取都将遵循内存中的先前写入。任何写入都将遵循先前从内存中读取的操作。因此任何使用易失性的线程都将始终看到最新的值。
AtomicReference 使用了 volatile 的这个属性。

以下是 AtomicReference 的一些源代码。
AtomicReference 指的是对象引用。该引用是 AtomicReference 实例中的一个易失性成员变量,如下所示。

private volatile V value;

get() 只是返回变量的最新值(就像 volatile 以“发生在之前”的方式所做的那样)。

public final V get()

以下是AtomicReference最重要的方法。

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

compareAndSet(expect,update)方法调用Java的unsafe类的compareAndSwapObject()方法。 unsafe 的此方法调用会调用本机调用,该调用会调用处理器的单个指令。 “expect”和“update”各自引用一个对象。

当且仅当 AtomicReference 实例成员变量“value”引用“expect”引用的同一个对象时,现在将“update”赋值给该实例变量,并返回“true”。否则返回 false。整个事情都是原子完成的。没有其他线程可以在其间拦截。
由于这是单处理器操作(现代计算机体系结构的魔力),因此它通常比使用同步块更快。但是请记住,当需要原子更新多个变量时,AtomicReference 将无济于事。

我想添加一个完整的运行代码,它可以在 eclipse 中运行。它将消除许多困惑。这里有 22 个用户(MyTh 线程)正在尝试预订 20 个座位。以下是代码片段,后面是完整代码。

代码片段,其中 22 个用户尝试预订 20 个座位。

for (int i = 0; i < 20; i++) {// 20 seats
    seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
    ths[i] = new MyTh(seats, i);
    ths[i].start();
}

以下是完整的运行代码。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Solution {

    static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
                                                // list index

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        seats = new ArrayList<>();
        for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }
        for (Thread t : ths) {
            t.join();
        }
        for (AtomicReference<Integer> seat : seats) {
            System.out.print(" " + seat.get());
        }
    }

    /**
     * id is the id of the user
     * 
     * @author sankbane
     *
     */
    static class MyTh extends Thread {// each thread is a user
        static AtomicInteger full = new AtomicInteger(0);
        List<AtomicReference<Integer>> l;//seats
        int id;//id of the users
        int seats;

        public MyTh(List<AtomicReference<Integer>> list, int userId) {
            l = list;
            this.id = userId;
            seats = list.size();
        }

        @Override
        public void run() {
            boolean reserved = false;
            try {
                while (!reserved && full.get() < seats) {
                    Thread.sleep(50);
                    int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
                                                                            // seats
                                                                            //
                    AtomicReference<Integer> el = l.get(r);
                    reserved = el.compareAndSet(null, id);// null means no user
                                                            // has reserved this
                                                            // seat
                    if (reserved)
                        full.getAndIncrement();
                }
                if (!reserved && full.get() == seats)
                    System.out.println("user " + id + " did not get a seat");
            } catch (InterruptedException ie) {
                // log it
            }
        }
    }

}    

I won't talk much. Already my respected fellow friends have given their valuable input. The full fledged running code at the last of this blog should remove any confusion. It's about a movie seat booking small program in multi-threaded scenario.

Some important elementary facts are as follows.
1> Different threads can only contend for instance and static member variables in the heap space.
2> Volatile read or write are completely atomic and serialized/happens before and only done from memory. By saying this I mean that any read will follow the previous write in memory. And any write will follow the previous read from memory. So any thread working with a volatile will always see the most up-to-date value.
AtomicReference uses this property of volatile.

Following are some of the source code of AtomicReference.
AtomicReference refers to an object reference. This reference is a volatile member variable in the AtomicReference instance as below.

private volatile V value;

get() simply returns the latest value of the variable (as volatiles do in a "happens before" manner).

public final V get()

Following is the most important method of AtomicReference.

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

The compareAndSet(expect,update) method calls the compareAndSwapObject() method of the unsafe class of Java. This method call of unsafe invokes the native call, which invokes a single instruction to the processor. "expect" and "update" each reference an object.

If and only if the AtomicReference instance member variable "value" refers to the same object is referred to by "expect", "update" is assigned to this instance variable now, and "true" is returned. Or else, false is returned. The whole thing is done atomically. No other thread can intercept in between.
As this is a single processor operation (magic of modern computer architecture), it's often faster than using a synchronized block. But remember that when multiple variables need to be updated atomically, AtomicReference won't help.

I would like to add a full fledged running code, which can be run in eclipse. It would clear many confusion. Here 22 users (MyTh threads) are trying to book 20 seats. Following is the code snippet followed by the full code.

Code snippet where 22 users are trying to book 20 seats.

for (int i = 0; i < 20; i++) {// 20 seats
    seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
    ths[i] = new MyTh(seats, i);
    ths[i].start();
}

Following is the full running code.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Solution {

    static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
                                                // list index

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        seats = new ArrayList<>();
        for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }
        for (Thread t : ths) {
            t.join();
        }
        for (AtomicReference<Integer> seat : seats) {
            System.out.print(" " + seat.get());
        }
    }

    /**
     * id is the id of the user
     * 
     * @author sankbane
     *
     */
    static class MyTh extends Thread {// each thread is a user
        static AtomicInteger full = new AtomicInteger(0);
        List<AtomicReference<Integer>> l;//seats
        int id;//id of the users
        int seats;

        public MyTh(List<AtomicReference<Integer>> list, int userId) {
            l = list;
            this.id = userId;
            seats = list.size();
        }

        @Override
        public void run() {
            boolean reserved = false;
            try {
                while (!reserved && full.get() < seats) {
                    Thread.sleep(50);
                    int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
                                                                            // seats
                                                                            //
                    AtomicReference<Integer> el = l.get(r);
                    reserved = el.compareAndSet(null, id);// null means no user
                                                            // has reserved this
                                                            // seat
                    if (reserved)
                        full.getAndIncrement();
                }
                if (!reserved && full.get() == seats)
                    System.out.println("user " + id + " did not get a seat");
            } catch (InterruptedException ie) {
                // log it
            }
        }
    }

}    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文