JAVA并发编程:三大特性-原子性

生活

生活就是生下来,活下去。

————

在JAVA并发编程,如果要保证程序的线程安全,就要保证代码的原子性、可见性、有序性。
今天这一篇先来聊聊原子性。

什么是原子性?

原子性,即一个操作或多个操作,要么全部执行并且在执行的过程中不被打断,要么全部不执行。(提供了互斥访问,在同一时刻只有一个线程进行访问)

JAVA本身的原子性

由JAVA内存模型来直接保证具有原子性变量操作 的有read/load/use/assign/store/write,也就是说对于基本数据类型的赋值,读取操作时具有原子性的。(在32位系统下对于long类型的赋值读取并不是原子性的)。

下面具有原子性的操作有?

x=1;
y=x;
x++;
x=x+1;

第一条 x =1,是一个单纯的赋值操作,满足原子性。
第二条 y=x ,实际是两个操作,分别是 读取x变量 ,将x赋值给y,这两个操作分别来看都是原子性的,但是合起来就不是了。
第三条 x++,实际是三个操作 ,先读取变量 ,在进行+1操作 ,再赋值给x,不满足原子性
第四条 x=x+1 同上,不满足原子性

看个不满足原子性的案例


public class YZXTest {

	public static void main(String[] args) {
		 CountDownLatch latch = new CountDownLatch(1000);
		for(int i=0;i<1000;i++) {
			new Thread(new Inc(latch)).start();
		}
		
		
		try {
			latch.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println(Inc.num);

	}
	
	static class Inc implements Runnable{
		
		private CountDownLatch latch;
		
		public static int num;

		@Override
		public void run() {
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			num++;
			
				latch.countDown();
			
		}

		public Inc(CountDownLatch latch) {
			super();
			this.latch = latch;
		}
		
	}

}

期望值1000,实际上跑了几次都在1000以下,说明num++这个操作不满足原子性。

JAVA提供了原子性的技术保障有如下:

1、synchronized (互斥锁)
2、Lock(互斥锁)
3、原子类(CAS 乐观锁)

上面两个都是通过互斥锁实现,即同一时刻只允许一个线程操作该变量,保障了原子性,没啥好说的。
原子类的实现可以看下。
int变量对应的原子类 AtomicInteger
所以如下的代码,通过原子类来实现原子性,具体代码修改如下:
public static int num public static AtomicInteger num = new AtomicInteger(0);
num++ num.incrementAndGet();
System.out.println(Inc.num); System.out.println(Inc.num.get());

结果跑了几次都是1000,说明确实实现了原子性。
原子类进行++的操作细节都在 incrementAndGet里,简单的看下源码:
调用到unsafe里的方法

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }
    其实就是通过乐观锁的方式实现。

原子类的优点:
不需要加锁,性能好。
缺点:
在高并发场景下,CAS失败的概率很高,长时间自旋导致CPU消耗较大

后记

埋两个坑
关于乐观锁、悲观锁。
关于CAS的ABA问题 后续再聊。

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84503466