并发编程的三大特性--原子性

Java内存模型:JMM(Java Memory Model )

为了解决相同代码在不同操作系统上出现的各种问题,引入了JMM来屏蔽不同的硬件、操作系统带来的差异

JMM规定所有的变量都会存储在主内从中,操作时,需要复制一份到线程内存(CPU内存),在线程内部做计算,然后再写会主内存(不一定会及时操作)。

原子性:一个不可分割、不可中断的操作,一个线程在执行时其他线程不会影响他

// 以下代码用于测试count++的操作时非原子性操作,多线程同时操作的时候,总数比200小
private static int count;
public static void increment(){
    Thread.sleep(10);
    count++;
}
public static void main(String[] args){
    Thread t1 = new Thread(()->{
        for(int i=0;i<100;i++){
            increment();
        }

    })
    Thread t2 = new Thread(()->{
        for(int i=0;i<100;i++){
            increment();
        }
    })
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(count);
}

++的操作,先从主内存拿到数据保存到cpu的寄存器中,在寄存器中进行+1操作,之后再写回主内存中。

如何保证并发编程的原子性

  1. 可以在方法上使用synchorinized关键字或使用同步代码块的方式。monitorenter与monitorexit实现锁资源的竞争,保证同一时刻只会有一个线程进入操作

  1. CAS(compare and swap)比较和交换,是CPU的一条原语,他在替换内存的某个值时,首先查看内存中的值是否与预期值一致,如果一致则执行替换操作。在Java中提供了Unsafe类实现CAS的操作方法。

    // 使用java提供的CAS方式实现原子性操作
    private static AtomicInteger count = new AtomicInteger(0);
    public static void increment(){
       Thread.sleep(10);
       count.incrementAndGet();
    }
    public static void main(String[] args){
       Thread t1 = new Thread(()->{
           for(int i=0;i<100;i++){
               increment();
           }
    
       })
       Thread t2 = new Thread(()->{
           for(int i=0;i<100;i++){
               increment();
           }
       })
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println(count);
    }

CAS的缺点:

  • 只能保证对一个变量的操作时原子性的,无法实现对多行代码实现原子性。

CAS的问题:

ABA问题,可以通过引入版本号的方式解决ABA问题,java中提供了一个类在CAS操作时通过追加版本号操作:AtomicStampedReference

引入AtomicStampedReference解决ABA问题

自旋时间过长问题:

  • 可以指定CAS一共循环多少次,如果超过次数则直接失败/挂起线程

  • 可以在CAS一次失败后,将这个操作暂存起来,后续获取结果时,将暂存的结果全部执行完,再返回最后的结果。

Lock锁,在并发数多时,使用Reentrantlock性能会好一些。Reentrantlock 底层基于AQS实现,有一个基于CAS维护的变量state 来实现锁的操作

private static Reentrantlock lock = new Reentrantlock();
lock.lock()
// 执行逻辑
lock.unLock();

ThreadLocal保证原子性的方式是不让多线程去操作临界资源,让每个线程去操作属于自己的数据。

实现原理:

  • 每个ThreadLocal中存储着一个变量:ThreadLocalMap

  • ThreadLocal本身不存储数据,而是一个工具类,提供了操作ThreadLocalMap的方法

  • ThreadLocal本身就是基于Entry[]实现的,一个线程可以存储多个ThreadLocal

  • 每一个线程都有自己的ThreadLocalMap,再基于ThreadLocal作为key,对value进行存取,ThreadLocalMap与ThreadLocal之间是弱引用,即若tl1引用不存在的情况下,ThreadLocal1对象会被GC回收

ThreadLocal内存泄漏问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值