无锁
无锁原理
-
CAS
-
CPU指令
这个CPU指令仅仅只是一条指令
AtomicInteger
看两个方法
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
看个demo示例
package someTest;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
static AtomicInteger i=new AtomicInteger();
public static class AddThread implements Runnable{
public void run() {
for(int k=0;k<10000;k++) {
i.incrementAndGet();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] ts=new Thread[10];
for(int k=0;k<10;k++) {
ts[k]=new Thread(new AddThread());
}
for(int k=0;k<10;k++) {ts[k].start();}
for(int k=0;k<10;k++) {ts[k].join();}
System.out.println(i);
}
}
上面类中定义一个AtomicInteger类型的变量i,分别开启10个线程对这个变量进行操作,最终输出的结果为100000,结果正确
AtomicReference
如果希望多线程环境下对一个对象的引用的修改是安全的话,就可以使用AtomicReference
demo:
package someTest;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest {
public static final AtomicReference atomicStr=new AtomicReference("abc");
public static void main(String[] args) {
for(int i=0;i<10;i++) {
new Thread(){
public void run() {
try {
Thread.sleep((int)Math.abs(Math.random()*100));
}catch (InterruptedException e) {
e.printStackTrace();
}
if(atomicStr.compareAndSet("abc", "def")) {
System.out.println("Thread:"+Thread.currentThread().getId()+"change value to def");
}else {
System.out.println("Thread:"+Thread.currentThread().getId()+"failed");
}
}
}.start();
}
}
}
AtomicStampedReference
这个类主要解决什么问题呢?ABA问题
看个demo:
package someTest;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceDemo {
static AtomicStampedReference money=new AtomicStampedReference(19, 0) ;
public static void main(String[] args) {
//模拟多个线程同时更新后台数据库,为用户充值
for(int i=0;i<3;i++) {
final int timestamp=money.getStamp();
new Thread() {
public void run() {
while(true) {
while(true) {
Integer m=money.getReference();
if(m<20) {
if(money.compareAndSet(m, m+20, timestamp, timestamp+1)) {
System.out.println("余额小于20,充值成功,余额:"+money.getReference()+"元");
break;
}
}else {
break;
}
}
}
}
}.start();
}
//用户消费线程,模拟消费行为
new Thread() {
public void run() {
for(int i=0;i<100;i++) {
while(true) {
int timeStamp=money.getStamp();
Integer m=money.getReference();
if(m>10) {
System.out.println("大于10元");
if(money.compareAndSet(m, m-10, timeStamp, timeStamp+1)) {
System.out.println("成功消费10元,余额:"+money.getReference());
break;
}
}
}
}
}
}.start();
}
}
输出为:
余额小于20,充值成功,余额:39元
大于10元
成功消费10元,余额:29
大于10元
成功消费10元,余额:19
大于10元
成功消费10元,余额:9
解决了个什么问题?
当用户的钱小于20的时候,生产者会为用户充钱,当用户的钱多于10块的时候,消费者线程就会消费用户的钱,如果不加时间戳,那么就会发生生产者线程一直为用户充钱的行为。(最初是19元,A状态,先充20,变成39元,B状态,这是,而后消费者线程消费两次,又变成了19元,A状态)
AtomicIntegerArray
这个类包装的数组,每个位置上的元素都是线程安全的,看个demo
package someTest;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
static AtomicIntegerArray arr=new AtomicIntegerArray(10);
public static class AddThread implements Runnable{
@Override
public void run() {
for(int k=0;k<100000;k++) {
arr.getAndIncrement(k%arr.length());
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] ts=new Thread[10];
for(int k=0;k<10;k++) {
ts[k]=new Thread(new AddThread());
}
for(int k=0;k<10;k++) {ts[k].start();}
for(int k=0;k<10;k++) {ts[k].join();}
System.out.println(arr);
}
}
结果输出:
[100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000]
可以看出,每个位置上的元素最终的值都是正确的。
AtomicIntegerFieldUpdater
这个东西很好用,为什么呢,因为他可以让类中的普通成员变量享受原子操作,有什么好处呢?当一个类已经定型,并且已经投入使用很长时间,可能会有很多依赖,如果现在我们想对其中某个成员变量进行原子操作保证线程安全怎么办?难道要去改属性修饰符吗?不不,改动可能很大,那么此时AtomicIntegerFieldUpdater就有用了,它可以通过反射的方式获得类的属性信息等,对这些需要原子操作的属性进行增强或者说包装,它就可以享受原子操作了
看个例子:
package someTest;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo {
public static class Candidate{
int id;
volatile int score;
}
public final static AtomicIntegerFieldUpdater scoreUpdater=
AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
//检查Updater是否工作正确
public static AtomicInteger allScore=new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Candidate stu=new Candidate();
Thread[] ts=new Thread[10000];
for(int i=0;i<10000;i++) {
ts[i]=new Thread() {
public void run() {
if(Math.random()>0.4) {
scoreUpdater.incrementAndGet(stu);
allScore.incrementAndGet();
}
}
};
ts[i].start();
}
for(int i=0;i<10000;i++) {ts[i].join();}
System.out.println("score="+stu.score);
System.out.println("allscore="+allScore);
}
}
输出:
score=6030
allscore=6030