-
Notifications
You must be signed in to change notification settings - Fork 45.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AtomicMarkableReference 不能解决ABA问题 #626
Comments
。。。刚看到这个issue。你的用法不对,mark只是用来标记是否cas过的,正常情况下别重新设置值,除非要循环使用。稍后我根据你的案例写个用法。 |
之前理解错了,重新写了份案例。 import java.util.concurrent.atomic.AtomicMarkableReference;
public class AtomicMarkableReferenceDemo {
private static final boolean DEFAULT_MARK_FLAG = false;
private static AtomicMarkableReference amr = new AtomicMarkableReference(100, DEFAULT_MARK_FLAG);
public static void main(String[] args) {
Thread refT2 = new Thread(() -> {
final boolean marked = amr.isMarked();
final Integer reference = (Integer) amr.getReference();
System.out.println("refT2 currentValue=" + amr.getReference() + ", currentMark=" + marked);
// 这段目的:模拟处理其他业务花费的时间
safeSleep(300);
// 别的线程操作时,根据匹配之前的(值、mark)和最新的(值、mark),如果同值,且 mark 不为默认值就是执行过了
if (reference.equals(amr.getReference())
&& amr.isMarked() == DEFAULT_MARK_FLAG) { // fixed: 最新的 mark 为 默认值时才进行 cas
System.out.println("refT2 currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked()
+ ", oldValue=" + reference + ", oldMark=" + marked);
boolean casResult = amr.compareAndSet(reference, reference + 1, DEFAULT_MARK_FLAG, !DEFAULT_MARK_FLAG);
System.out.println("refT2 cas result --------> " + casResult);
}
System.out.println("refT2 ending ...");
});
refT2.start();
// 这段目的:为了让 refT2 线程先跑起来
safeSleep(100);
Thread refT1 = new Thread(() -> {
// 同个线程操作时设置 mark 为 默认值的取反,且之后不要改变
final Integer reference = (Integer) amr.getReference();
System.out.println("refT1 currentValue=" + reference + ", currentMark=" + amr.isMarked());
boolean casResult = amr.compareAndSet(reference, reference + 1, DEFAULT_MARK_FLAG, !DEFAULT_MARK_FLAG);
System.out.println("refT1 cas result --------> " + casResult);
final Integer reference2 = (Integer) amr.getReference();
final boolean marked = amr.isMarked();
System.out.println("refT1 currentValue=" + reference2 + ", currentMark=" + marked);
boolean casResult2 = amr.compareAndSet(reference2, reference2 - 1, marked, marked);
System.out.println("refT1 cas2 result --------> " + casResult2);
System.out.println("refT1 currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
System.out.println("refT1 ending ...");
});
refT1.start();
}
private static void safeSleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} 理论上输出是这样的:
|
@jinyahuan @Snailclimb hello,看了你的代码,我们之间的理解有点误差,我分析下我的用法,也欢迎大家参与讨论。 |
@itmaaa 常规情况下是不会做出ABA操作的,往往都是远离原值变化的(就像AtomicInteger一样,用到的都是incrementAndGet),不会改变了原值之后还还原的。 当然主要还是生产中没遇到过有ABA操作的情况,只能靠模拟,我也不能说这样做就是合适的
|
上面的方案感觉还是不可行(变回原值后hashcode也应该会相同),等下给划掉,可能还是太菜了- - 这里再给一种方案,理论上所有Atomic类都适用,加上一个 volatile long 值的 modCount,类似于 fail-fast机制。 不过这种机制就是 |
@jinyahuan 现实场景中会不会做出ABA操作主要还是看业务的,如果把我例子看成银行转账,100改101看成入账,101改100看成扣款,对于refT2 去操作时发现还是100(标记也符合)就改成101了,解决ABA问题主要在于保证获取标记后到更新期间不被更改,至于出现ABA问题会不会对业务造成影响(需不需要解决),还是看实际场景的,我个人这么理解 |
恩,看实际应用场景。 |
AtomicMarkableReference 我认为它压根就没想解决ABA问题 |
/**
AtomicMarkableReference则是将一个boolean值作是否有更改的标记,本质就是它的版本号只有两个,true和false,
修改的时候在这两个版本号之间来回切换,这样做并不能解决ABA的问题,只是会降低ABA问题发生的几率而已
@author : mazh
@Date : 2020/1/17 14:41
*/
public class SolveABAByAtomicMarkableReference {
private static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(100,false);
public static void main(String[] args) {
}
}
个人理解是这样,不知是否正确?
The text was updated successfully, but these errors were encountered: