java并发编程 第二期 CAS

上期提到过AtomicInteger ,其中一个赋值方法compareAndSet(),CAS就是原子类比较并设值方法的缩写。

CAS 全程是CompareAndSwap 比较并交换,是一条CPU原语,这个过程是原子的

CAS 简单实用

package com.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liuxu
 * @date 2021/11/10 21:59
 */
public class CasDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(10);
        System.out.println(atomicInteger.compareAndSet(10,100)+"当前值是:"+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(10,100)+"当前值是:"+atomicInteger.get());
    }
}

如果和预期值不一致,会导致值修改失败

CAS底层原理可以从

atomicInteger.incrementAndGet();看起

/*this 当前对象
valueOffset 内存偏移量
unsafe  unsafe类,cas关键
*/
public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }




unsafe类下方法
var1 当前对内存偏移量
var2 内存偏移量
var4 增加值
 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2); //相当于从主物理内存拿到当前对象物理内存上的值 var5
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }
//这个方法在java中看不到实现,可以在java源码包中找到,sun.misc包下可以看到源码
/*
*这个方法首先会将 内存偏移量得到的值var5 和 原来的值var1进行比较 如果相同,说明当前
*线程备份值和内存中的值相同(这里不明白可以看第一期)JMM模型中线程操作内存的方法,可以进行值的增加,
*返回ture,在原来值上增加var4,操作成功,由于方法native修饰,此方法线程安全
*如果失败,只能重新获取内存偏移量上的值,直到设置成功。
*/
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

CAS的缺点

1.如果一个线程一直未CAS设值成功,就会while循环,可能会给CPU带来较大开销

2.会存在ABA问题,如果A线程拷贝了主内存中的值,但是还没有进行值的修改,

此时B线程将主线程中的值由A改成B,然后再改成A,此时如果A线程进行CAS操作,

会认为主线程的值并没有修改

3.可以使用的值比较单一

原子引用可以解决这个问题,首先他可以将对对象封装成原子类

package com.cas;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author liuxu
 * @date 2021/11/10 22:42
 */
public class AtomDemo {
    public static void main(String[] args) {
        AtomicReference<String> reference = new AtomicReference<>();
        reference.set("张三");
        System.out.println(reference.get());
        boolean b = reference.compareAndSet("张三", "zhang");
        System.out.println(b+reference.get());
        boolean c = reference.compareAndSet("张三", "zhang");
        System.out.println(c+reference.get());
    }
}

版本号引用,解决ABA问题

package com.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author liuxu
 * @date 2021/11/10 22:51
 */
public class ABADemo {
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(0);//原子引用
    static AtomicStampedReference<Integer> stampedReference =
            new AtomicStampedReference<>(0,1);//版本号原子引用

    public static void main(String[] args) {
        new Thread(()->{
            atomicReference.compareAndSet(0,100);
            atomicReference.compareAndSet(100,0);
        },"A").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = atomicReference.compareAndSet(0, 100);
            System.out.println(Thread.currentThread().getName()+"设置值"+b);

        },"B").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("以上说明ABA问题存在,版本号原子引用可以解决");
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            stampedReference.compareAndSet(0,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
            stampedReference.compareAndSet(100,0,stampedReference.getStamp(),stampedReference.getStamp()+1);
        },"C").start();

        new Thread(()->{

            int stamp = stampedReference.getStamp();

            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean andSet = stampedReference.compareAndSet(0, 2009, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"获取的版本号是"+stamp);
            System.out.println(Thread.currentThread().getName()+"实际的版本号是"+stampedReference.getStamp());
            System.out.println(Thread.currentThread().getName()+"是否修改成功"+andSet);


        },"D").start();

    }
}

CountDownLatch 用于多线程间递减计数,唤醒另一线程。

一个或者多个线程功来调用 countDownLatch.await(); 如果 countDownLatch不是0就会被阻塞

直到 countDownLatch.countDown();将初始值递减到0

package com.cas;

import java.util.concurrent.CountDownLatch;

/**
 * @author liuxu
 * @date 2021/11/11 20:20
 */

/**
 * CountDownLatch  countDownLatch.countDown()递减计数,
 * 直到为0 countDownLatch.await();才会放行countDownLatch.await()所在线程
 */
public class CountDownLathDemo {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(10);


        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                countDownLatch.countDown();
                System.out.println("第"+Thread.currentThread().getName()+"个人离开");
            },i+"").start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("闭门关灯");
    }
}

CyclicBarrier

CyclicBarrier构造时声明最终目标值和要运行的方法,

cyclicBarrier.await();一次,cyclicBarrier从0自增1,达到目标值,lamda表达式中的方法运行

package com.cas;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author liuxu
 * @date 2021/11/11 20:50
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
           System.out.println("葫芦小金刚出世");
        });

        for (int i = 0; i < 7; i++) {
            new Thread(()->{
                System.out.println("葫芦娃老"+Thread.currentThread().getName()+"来了");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },i==0?"大":i+1+"").start();


        }
    }
}

Semaphore

用于多个共享资源的互斥 用于并发线程数的控制

用于类似抢车位式占用资源

首先new 2个信号量,最多两个人同时用车

semaphore.acquire(); 占用信号量,最多两个线程同时占用

占用后任务执行完毕finally semaphore.release();释放信号量资源

下一个线程可以立马抢占

package com.cas;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author liuxu
 * @date 2021/11/11 21:04
 *
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore =new Semaphore(2);
        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName()+"占用车位后释放");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();

                }
            },i+"").start();
        }



    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值