Chi*_*ntu 290 java multithreading
我们什么时候使用AtomicReference?
是否需要在所有多线程程序中创建对象?
提供一个应该使用AtomicReference的简单示例.
and*_*soj 196
原子引用应该用在需要对引用执行简单原子(即线程安全,非平凡)操作的设置中,基于监视器的同步不适合.假设您要检查特定字段是否仅在上次检查时对象的状态仍然存在时:
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,cachedValueToUpdate);
Run Code Online (Sandbox Code Playgroud)
由于原子引用语义,即使cache在线程之间共享对象而不使用,也可以执行此操作synchronized.一般来说,除非你知道自己在做什么,否则最好使用同步器或java.util.concurrent框架而不是裸露Atomic*.
两个优秀的死树引用,将向您介绍此主题:
注意(我不知道这是否一直都是真的)引用赋值(即=)本身是原子的(更新原始的 64位类型,long或者double可能不是原子的;但更新引用总是原子的,即使它是64位)没有明确使用Atomic*.
请参阅Java语言规范3ed,第17.7节.
Eri*_*ren 84
当您需要在多个线程之间共享和更改不可变对象的状态时,原子引用是理想的选择.这是一个超级密集的声明,所以我会稍微分解一下.
首先,不可变对象是在构造之后实际上不会改变的对象.通常,不可变对象的方法返回同一个类的新实例.一些例子包括Long和Double的包装类,以及String,仅举几例.(根据JVM上的Programming Concurrency,不可变对象是现代并发的关键部分).
接下来,为什么AtomicReference比共享该共享值的volatile对象更好.一个简单的代码示例将显示差异.
volatile String sharedValue;
static final Object lock=new Object();
void modifyString(){
synchronized(lock){
sharedValue=sharedValue+"something to add";
}
}
Run Code Online (Sandbox Code Playgroud)
每次要根据当前值修改该volatile字段引用的字符串时,首先需要获取该对象的锁定.这可以防止其他一些线程在此期间进入并更改新字符串连接中间的值.然后当你的线程恢复时,你破坏了另一个线程的工作.但老实说,代码会起作用,看起来很干净,这会让大多数人开心.
轻微问题.这很慢.特别是如果有很多争用的锁对象.这是因为大多数锁需要OS系统调用,并且您的线程将阻塞并从CPU切换出上下文以便为其他进程腾出空间.
另一种选择是使用AtomicRefrence.
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()+"lets add something";
// Compare and set
success=shared.compareAndSet(prevValue,newValue);
}
Run Code Online (Sandbox Code Playgroud)
现在为什么这样更好?老实说,代码比以前干净一点.但是在AtomicRefrence的引擎盖下发生了一些非常重要的事情,那就是比较和交换.它是单CPU指令,而不是OS调用,使切换发生.这是CPU上的单个指令.并且因为没有锁,所以在锁被运用的情况下没有上下文切换,这节省了更多的时间!
问题是,对于AtomicReferences,它不使用.equals()调用,而是使用==比较预期值.因此,请确保期望是从循环中获取的实际对象.
Bin*_*ati 27
以下是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());
}
}
Run Code Online (Sandbox Code Playgroud)
setLower和setUpper都是check-then-act序列,但它们没有使用足够的锁定来使它们成为原子序列.如果数字范围成立(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 final AtomicReference<IntPair> values =
new AtomicReference<IntPair>(new IntPair(0, 0));
public int getLower() { return values.get().lower; }
public int getUpper() { return values.get().upper; }
public void setLower(int i) {
while (true) {
IntPair oldv = values.get();
if (i > oldv.upper)
throw new IllegalArgumentException(
"Can't set lower to " + i + " > upper");
IntPair newv = new IntPair(i, oldv.upper);
if (values.compareAndSet(oldv, newv))
return;
}
}
// similarly for setUpper
}
Run Code Online (Sandbox Code Playgroud)
Ham*_*riZ 20
应用乐观锁时,可以使用AtomicReference.您有一个共享对象,并且您想要从多个线程更改它.
由于其他线程可能已修改它并且/可以在这两个步骤之间进行修改.您需要在原子操作中执行此操作.这是AtomicReference可以提供帮助的地方
我们什么时候使用 AtomicReference?
AtomicReference是一种灵活的方式,可以在不使用同步的情况下以原子方式更新变量值。
AtomicReference 支持对单个变量进行无锁线程安全编程。
有多种方法可以通过高级并发API实现线程安全。原子变量是多个选项之一。
Lock 对象支持简化许多并发应用程序的锁定习惯用法。
Executors定义用于启动和管理线程的高级 API。java.util.concurrent 提供的 Executor 实现提供了适合大型应用程序的线程池管理。
并发集合可以更轻松地管理大型数据集合,并且可以大大减少对同步的需求。
原子变量具有最小化同步并有助于避免内存一致性错误的功能。
提供一个应该使用 AtomicReference 的简单示例。
示例代码AtomicReference:
String initialReference = "value 1";
AtomicReference<String> someRef =
new AtomicReference<String>(initialReference);
String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);
Run Code Online (Sandbox Code Playgroud)
是否需要在所有多线程程序中创建对象?
你不必使用 AtomicReference在所有多线程程序中使用。
如果要保护单个变量,请使用AtomicReference. 如果要保护代码块,请使用其他结构,例如Lock/synchronized等。
我不会说太多。我尊敬的朋友们已经提供了宝贵的意见。本博客最后的完整运行代码应消除任何混乱。这是关于在多线程场景中预订电影座位的小程序。
一些重要的基本事实如下。1>不同的线程只能竞争堆空间中的实例变量和静态成员变量。2>易失性读或写完全是原子的且已序列化/发生过,并且只能从内存中进行。说这是我的意思是任何读取都将跟随内存中的先前写入。并且任何写操作都将遵循先前从内存中读取的内容。因此,任何使用volatile的线程都将始终看到最新的值。 AtomicReference使用volatile的此属性。
以下是AtomicReference的一些源代码。AtomicReference引用对象引用。该引用是AtomicReference实例中的一个volatile成员变量,如下所示。
private volatile V value;
Run Code Online (Sandbox Code Playgroud)
get()仅返回变量的最新值(就像volatile以“ happens before”的方式一样)。
public final V get()
Run Code Online (Sandbox Code Playgroud)
以下是AtomicReference的最重要方法。
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
Run Code Online (Sandbox Code Playgroud)
compareAndSet(expect,update)方法调用Java的不安全类的compareAndSwapObject()方法。这种不安全的方法调用会调用本地调用,该本地调用会向处理器调用一条指令。“期望”和“更新”均引用一个对象。
仅当AtomicReference实例成员变量“值”所引用的对象相同时,才将“更新”分配给该实例变量,并返回“ 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();
}
Run Code Online (Sandbox Code Playgroud)
以下是完整的运行代码。
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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个非常简单的用例,与线程安全无关。
要在lambda调用之间共享对象,AtomicReference可以使用选项:
public void doSomethingUsingLambdas() {
AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();
soSomethingThatTakesALambda(() -> {
yourObjectRef.set(youObject);
});
soSomethingElseThatTakesALambda(() -> {
YourObject yourObject = yourObjectRef.get();
});
}
Run Code Online (Sandbox Code Playgroud)
我并不是说这是好的设计或其他任何东西(这只是一个简单的例子),但是如果您遇到需要在lambda调用之间共享对象的情况,AtomicReference则可以选择。
实际上,您可以使用包含引用的任何对象,甚至可以使用仅包含一项的Collection。但是,AtomicReference非常适合。
| 归档时间: |
|
| 查看次数: |
128413 次 |
| 最近记录: |