java高并发之CAS无锁
无锁类的原理
1.CAS
CAS(Compare andSwap)方法包含三个参数(V,E,N) :V表示要更新的变量,E表示预期的值,N表示新值。仅当V的值等于E时,才会将V的值修改为N。如果V的值不等于E,说明已经被其他线程修改了,当前线程可以放弃此操作,也可以再次尝试次操作直至修改成功。基于这样的算法,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
2.CPU指令
有的人会问了CAS操作是不是有Bug, 由于CAS操作时会先读取一个值,然后比较,然后再设置,他就会认为数据在读取比较之后还未进行设值之前,这个时候有个新的线程进来,把这个数据修改了,也就说他认为CAS步骤太多,担心在造作的时候被其它线程干扰,认为不是一个合适的方案。其实这个担心是多余的,因为CAS整的操作过程是原子操作(Atomic)它是由一条CPU指令完成的,并不是读数据取数据比较数据设置数据好多CPU指令完成的。
无锁类
1.AtomicInteger
我们进入AtomicInteger类里面可以看到很多方法
我们随便找一个方法看一看:
从注释中可以看出getAndincrement()方法返回一个当前值,并将当前值加1 。
这个方法这里可以看到 for的死循环,首先获取(get)当前值current,然后对着值进行加1,next值可能当前值(current)+1,也可能是已经被改过的值。然后进行判断,如果next不是当前值(current)+1,则继续For循环;如果是当前值(current)+1,则返回当前值(current)。
package com.nliki.www;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
static AtomicInteger i=new AtomicInteger();
public static class AddThread implements Runnable{
@Override
public void run(){
for(int k=0;k<10000;k++){
i.incrementAndGet();
}
}
}
/**
* @param args
*/
public static void main(String[] args) throws InterruptedException{
// TODO Auto-generated method stub
Thread[] t=new Thread[10];
for(int k=0;k<10;k++){
t[k]=new Thread(new AddThread());
}
for(int k=0;k<10;k++){
t[k].start();
}
for(int k=0;k<10;k++){
t[k].join();
}
System.out.println(i);
}
}
结果:
2.unsafe
unsafe从字面上就知道这是不安全的,java相对于C,C++来说,相对比较安全,因为它将有关指针一些内容屏蔽掉了,而unsafe恰恰相反,它相对于java底层它会提供一些类似指针的操作,unsafe主要应用在jdk内部,进行一些位运算。它提供的一般是根据偏移量设置值,我们可以在AtomicInteger看到这样的方法objectFieldOffset,objectFieldOffset类似于C中的结构体struct
比如:在C中有一个这样一个结构体struct,我们定义一个Int a、int b
而java里是对Class
3.AtomicReference
AtomicReference和AtomicInteger 相比,AtomicInteger 封装的是一个整数,而AtomicReference封装的是一个对象的引用,从AtomicReference我们可以看出public class AtomicReference<V>,V可以是任意模板,他也有一些类似AtomicInteger 方法。
package com.nliki.www;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceDemo {
public final static AtomicReference<String> atomicStr=new AtomicReference<String>("Nliki");
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
new Thread(){
public void run(){
try{
Thread.sleep(Math.abs((int)(Math.random()*100)));
}catch(InterruptedException e){
e.printStackTrace();
}
if(atomicStr.compareAndSet("Nliki", "NanHui")){
System.out.println("Thread:"+Thread.currentThread().getId()+" Change value to NanHui ");
}else{
System.out.println("Thread:"+Thread.currentThread().getId()+" FAILED");
}
}
}.start();
}
}
}
运行结果:
4.AtomicStampedReference
AtomicStampedReference从字面Stamped上可以看出他是一个带有邮戳的。下面我们可以AtomicStampedReference有什么用。如果下面这样张图我们没有用到AtomicStampedReference方法,我们是很难知道中间有值得改变。
package com.nliki.www;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtormicStampedReferenceDemo {
static AtomicStampedReference<Integer> money=new AtomicStampedReference<Integer>(19,0);
/**
* @param args
*/
public static void main(String[] args) throws InterruptedException{
// TODO Auto-generated method stub
for(int i=0;i<3;i++){
final int timestamp=money.getStamp();
new Thread(){
public void run(){
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;
}
}else{
break;
}
}
}
}.start();
new Thread(){
public void run(){
for(int i=0;i<20;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.getStamp());
break;
}
}else{
System.out.println("没有足够的金额");
break;
}
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}.start();
}
}
}
运行结果:
从运行结果中我们可以看到我们只充值了一次。
5.AtomicIntegerArray
AtomicIntegerArray 和AtomicInteger 区别 很显然是数组
package com.nliki.www;
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 i=0; i<100000;i++){
arr.getAndIncrement(i%arr.length());
}
}
}
/**
* @param args
*/
public static void main(String[] args)throws InterruptedException {
// TODO Auto-generated method stub
Thread[] ts=new Thread[10];
for(int i=0;i<10;i++){
ts[i]=new Thread(new AddThread());
}
for(int i=0;i<10;i++){ts[i].start();}
for(int i=0;i<10;i++){ts[i].join();}
System.out.println(arr);
}
}
运行结果:
6.AtomicIntegerFieldUpdater
package com.nliki.www;
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<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
//我们用AtomicInteger验证Updater是否正确
public static AtomicInteger allScore = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException{
final Candidate stu = new Candidate();
Thread[] t = new Thread[10000];
for (int i = 0, len = t.length; i < len; i++) {
t[i] = new Thread() {
public void run() {
if (Math.random() > 0.4) {
scoreUpdater.incrementAndGet(stu);
allScore.incrementAndGet();
}
}
};
t[i].start();
}
for (int i = 0, len = t.length; i < len; i++) {
t[i].join();
}
System.out.println("score="+stu.score);
System.out.println("allScore="+allScore);
}
}
运行结果:
上一篇: 单利模式及python实现方式详解
下一篇: 杜康酒价格是多少,杜康酒怎么样?