关闭

JUC之CAS

557人阅读 评论(0) 收藏 举报
分类:
JUC是java.util.concurrent包的简称,该包提供了并发编程的解决方案(当然,JAVA并发编程的解决方案还有synchronized)。从概括的层面来说,JUC包有两大核心:CAS和AQS。其中CAS是java.util.concurrent.atomic包的基础,AQS是java.util.concurrent.locks包以及一些常用类比如Semophore等类的基础。我们先来说说CAS。

CAS的全称为Compare-And-Swap,它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能。那么为什么CAS会出现呢?它的作用是怎样的?

实现并发的传统方式是加锁,JAVA中的锁有synchronized和Lock。Lock是基于AQS和CAS实现的,在此先不叙述。对于synchronized锁,JVM在执行它的时候会依赖操作系统的临界区机制。这样的话,每次执行到synchronized锁,都会经历用户态和内核态之间的切换。这个过程的消耗是很大的。而且,大多数时候synchronized锁住的操作是很细粒度的。为了细粒度的操作去经历用户态和内核态之间的切换是低效的做法。

说到这,我想到了线程池。大家知道,当线程创建和销毁的时间大于任务执行的时间时,就需要考虑使用线程池了。但如果和任务执行时间相比,线程创建和销毁的时间很少,那么线程池也可不用。

在synchronized中就是这个问题,当需要同步的操作粒度很细时,使用synchronized是不高效的,这时就有CAS存在的意义了。比如对于i++这种并发计数功能,使用synchronized就大材小用了,而使用CAS来实现就会更加的轻量级,性能更好。因此可以看到java.util.concurrent.atomic包中有类似AtomicInteger这种类。我们来看下AtomicInteger类的核心源码:
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
 
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
}


在上面的代码中Unsafe类负责执行CAS并发原语,由JVM转化为汇编。在代码中使用CAS自旋volatile变量的形式实现非阻塞并发。这种方式是CAS的主要使用方式。

CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置。无论如何,告诉我原值。

Java.util.concurrent.atomic包为开发者提供了比synchronized更加细粒度的并发代码控制方式。实际上也是最细粒度的并发代码控制方式。

CAS是乐观锁,是一种冲突重试的机制,在并发竞争不是很激烈的情况下(也是大多数情况),他的性能要好于基于锁的并发性能。因为并发竞争激烈的话,冲突重试的过程很多。

下面是基于AtomicReference实现的并发的非阻塞栈:
import java.util.concurrent.atomic.AtomicReference;

public class ConcurrentStack <E>{
	private AtomicReference<Node<E>> top=new AtomicReference<Node<E>>();
	public void push(E value){
		Node<E> newTop=new Node<E>(value);
		Node<E> oldTop=null;
		do{
			oldTop=top.get();
			newTop.next=oldTop;
		}while(!top.compareAndSet(oldTop,newTop));
	}
	public E pop(){
		Node<E> node=null;
		Node<E> newTop=null;
		do{
			node=top.get();
			if(node==null)
				return null;
			newTop=node.next;
			node.next=null;
		}while(!top.compareAndSet(node, newTop));
		return node.value;
	}	
	private class Node<E>{
		public Node<E> next;
		public final E value;
		public Node(E value){
			this.value=value;
		}
	}
}


CAS的典型步骤如下:
while(true){
	int A=V.get();//mark1
	if(A==V.compareAndSwap(A,newA))//mark2
		return A;
}


在上面代码的mark1行,首先获取V的值为A,然后在mark2行会重新判断V的值是否还是A。现实中有这样的情况:在mark1行之后mark2行之前,V的值可能从A变成B又变成A。这个时候,现有的代码仍然认为V的值没有改变,而有些情况下,我们需要识别V的这种改变。这就是ABA问题。

解决ABA问题的一种思路就是在CAS操作的时候更新两个值,包括引用和该值的版本号,JUC中提供了这个类AtomicStampedReferance以及AtomicMarkedReference。这两个类支持在两个变量上执行CAS操作,用于解决ABA问题。
0
0
查看评论

java并发包顶层AQS(抽象的队列式的同步器)分析,结合ReentrantLock分析(源码分析)

package com.kailong.lock; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.concurrent.TimeUnit; import...
  • likailonghaha
  • likailonghaha
  • 2017-04-20 16:40
  • 632

【java并发】基于JUC CAS原理,自己实现简单独占锁

synchronized的基本原理回顾 在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1.  synchronized是独占式的,拿到对象锁才...
  • jeff06143132
  • jeff06143132
  • 2013-08-19 00:21
  • 474

juc并发

Executor 框架是 juc 里提供的线程池的实现。前两天看了下 Executor 框架的一些源码,做个简单的总结。 线程池大概的思路是维护一个的线程池用于执行提交的任务。我理解池的技术的主要意义有两个: 1.  资源的控...
  • wjh823177094
  • wjh823177094
  • 2015-04-06 18:16
  • 655

Java多线程 -- JUC包源码分析4 -- 各种锁与无锁

说到锁,想必很多人就会被如下的各种专业名词绕晕了。本文试图厘清关于锁的各种概念,从而对锁有一个全面而深入的理解。–自旋锁/阻塞锁 –独占锁(排它锁)/共享锁(读写锁) –公平锁/非公平锁 –偏向锁/非偏向锁 –可重入锁 –悲观锁/乐观锁 –ReentrantLock源码分析 –Abst...
  • chunlongyu
  • chunlongyu
  • 2016-09-03 21:18
  • 1467

JUC之AQS框架

AQS是一个框架,一个提供锁或同步器依赖于`FIFO等待队列`所必要的“基础设施”的框架。**Dong Lea**之所以写个抽象类的目的是为了简化我们实现同步器的工作。 提供一个基于FIFO等待队列,可以用于构建锁或者其他同步装置的基础框架。意在能够成为实现大部分同步需求的基础。 AQS默认提供了...
  • zteny
  • zteny
  • 2017-02-08 00:38
  • 413

Java之JUC系列(01)--“原子性”基本概述

一、JUC基本介绍 JUC(Java Util Concurrency):Java并发工具类。 由上图可以知道,Java并发工具类主要包含了五个部分。Atomic : AtomicInteger、AtomicLong、AtomicReference Locks : Lock, Condition...
  • L_kanglin
  • L_kanglin
  • 2017-02-22 11:22
  • 316

Java多线程系列--“JUC集合”

转自:Java多线程系列--“JUC集合”01之 框架   概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类。接下来,将展开对JUC包中的集合进行学习。在学习之前,先温习一下"Java集合包&...
  • Together_CZ
  • Together_CZ
  • 2017-04-03 15:27
  • 691

cas 入门之八:cas 认证处理器-GENERIC

GENERIC(普通认证) 需要增加cas-server-support-generic-3.5.2.jar 普通认证很简单,就是将你需要认证的用户名、密码对存放在cas 服务端,一种式是直接配置在spring的配置中,另外一种是配置在 文件文件即文件系统的认证处理器, accept use...
  • zhurhyme
  • zhurhyme
  • 2014-06-26 20:31
  • 3297

java并发编程(十九)----(JUC集合)总体框架介绍

本节我们将继续学习JUC包中的集合类,我们知道jdk中本身自带了一套非线程安全的集合类,我们先温习一下java集合包里面的集合类,然后系统的看一下JUC包里面的集合类到底有什么不同。java集合类java集合类里面主要包含两大类:一类是Collection接口下的List、Set和Queue接口,一...
  • a953713428
  • a953713428
  • 2017-02-23 17:13
  • 625

CAS与ABA问题

CAS实现原子操作的三大问题import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class Counter { private ...
  • chuan_yu_chuan
  • chuan_yu_chuan
  • 2016-11-30 21:04
  • 359
    个人资料
    • 访问:150872次
    • 积分:1774
    • 等级:
    • 排名:千里之外
    • 原创:138篇
    • 转载:11篇
    • 译文:0篇
    • 评论:39条
    最新评论