java高并发之CAS无锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Nliki/article/details/80257310

无锁类的原理

1.CAS

      CASCompare 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);

	}

}

运行结果:


猜你喜欢

转载自blog.csdn.net/Nliki/article/details/80257310