CAS全称为Compare-And-Swap。它是一条CPU的原子指令,是硬件对于并发操作共享数据的支持。其作用是CPU在某个时刻比较两个值是否相等
核心原理:在操作期间CAS先比较下主存中的值和线程中工作内存中的值是否相等,如果相等才会将主存中的值更新为新值,不相等则不交换(如果不相等则会一直通过自旋方式尝试更新值)
CAS指令存在如下问题:
循环时间长开销大:如果CAS失败,则会一直通过自旋进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。因此冲突如果过于频繁的场景不建议使用CAS
原语进行处理(CAS
是乐观锁机制,乐观锁不适合冲突频繁的场景,这时候可以选择悲观锁的机制)。
package com.bierce;
public class TestCAS {
public static void main(String[] args) {
final CAS cas = new CAS();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
int expectedValue = cas.getValue();//每次更新前必须获取内存中最新的值,因为可能与compareAndSwap中第一次获取的Vaule不一样
boolean updateRes = cas.compareAndSet(expectedValue, (int) Math.random() * 101);
System.out.print(updateRes + " "); // true true true true true true true true true true
}).start();
}
}
}
class CAS{
private int value;
//获取内存值
public synchronized int getValue(){
return value;
}
//比较内存值
public synchronized int compareAndSwap(int expectedValue, int newValue){
int oldValue = value;//
if (oldValue == expectedValue){
this.value = newValue;
}
return oldValue;
}
//设置内存值
public synchronized boolean compareAndSet(int expectedValue, int newValue){
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
在多线程环境下我们可以通过加锁(synchronized/Lock)保证数据操作的一致性,但效率显得过于笨重。因此java提供java.util.concurrent.atomic
包,里面是原子操作的封装类,使用起来方便且高性能高效。Atomic 类方法没有任何加锁,底层核心就是通过 volatile 保证了线程可见性以及Unsafe类的CAS算法操作保证数据操作的原子性
package com.bierce;
import java.util.concurrent.atomic.AtomicInteger;
public class TestAtomicDemo {
public static void main(String[] args) {
AtomicDemo atomicDemo = new AtomicDemo();
//模拟多线程,通过原子类保证变量的原子性
for(int i =0; i < 10; i++){
new Thread(atomicDemo).start();
}
}
}
class AtomicDemo implements Runnable{
//AtomicInteger原子类提供很多API,根据需求使用
private AtomicInteger sn = new AtomicInteger();
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//getAndIncrement() :类似于 i++
//incrementAndGet() :类似于 ++i
//System.out.print(sn.getAndIncrement() + " "); // 输出:1 9 8 7 0 5 2 6 4 3
System.out.print(sn.incrementAndGet() + " "); // 输出:1 10 9 5 2 8 3 6 7 4
}
}
ConcurrentHashMap内部采用了“锁分段”思想替代HashTable的独占锁,增加了同步的操作来控制并发,且提高了性能,但JDK1.7&&JDK1.8版本中底层实现有所区别
package com.bierce;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* CopyOnWriteArrayList : 支持并发读取时写入
* 注意:添加数据操作多时,效率比较低,因为每次添加都会进行复制一个新的列表,开销大;
* 而并发迭代操作多时效率高
*/
public class TestCopyOnWriteArrayList {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayListDemo copyOnWriteArrayListDemo = new CopyOnWriteArrayListDemo();
for (int i = 0; i < 10; i++) {
new Thread(copyOnWriteArrayListDemo).start();
}
}
}
class CopyOnWriteArrayListDemo implements Runnable{
// 不支持并发修改场景,Exception in thread "Thread-1" java.util.ConcurrentModificationException
//private static List list = Collections.synchronizedList(new ArrayList());
public static CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
static{
list.add("贾宝玉");
list.add("林黛玉");
list.add("薛宝钗");
}
@Override
public void run() {
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
list.add("test");
}
}
}
CountDownLatch底层是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应的减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以继续执行任务
package com.bierce;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatchDemo {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);//声明5个线程
CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo(countDownLatch);
//long start = System.currentTimeMillis();
Instant start = Instant.now();//JDK8新特性API
for (int i = 0; i < 5; i++) { //此处循环次数必须和countDownLatch创建的线程个数一致
new Thread(countDownLatchDemo).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
}
//long end = System.currentTimeMillis();
Instant end = Instant.now();
//System.out.println("执行完成耗费时长: " + (end - start));
System.out.println("执行完成耗费时长: " + Duration.between(end, start).toMillis()); // 110ms
}
}
class CountDownLatchDemo implements Runnable{
private CountDownLatch countDownLatch;
public CountDownLatchDemo(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
synchronized (this){ //加锁保证并发安全
try {
for (int i = 0; i < 10000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}finally {
countDownLatch.countDown(); //线程完成后执行减一
}
}
}
}