关于线程安全

学习笔记,仅供自己参考,如有不对欢迎指正

1.关于内存模型

CPU高速缓存:因为CPU的执行速度要大于内存的读写速度,如果任何时候数据操作直接与内存交互,效率很低,所以出现了CPU高速缓存

CPU高速缓存的作用:程序执行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。

程序的原子性、可见性、有序性

  • 原子性:指一个操作是不可以中断的,不会被线程调度机制打断。
  • 可见性:指当一个线程修改共享变量的值时,其他线程会立刻知道这个指的修改
  • 有序性:指程序执行的顺序按照代码的先后顺序执行。

禁止指令重排
Java语言提供了volatilesynchronized两个关键字来保证线程之间操作的有序性
volatile关键字本身就包含了禁止指令重排序的语义
synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这个规则决定了持有同一个锁的两个同步块只能串行地进入。

参考:https://www.cnblogs.com/guanghe/p/9206635.html

2.关于线程安全

  • 为什么会有线程安全?
    当多个线程同时访问统一代码(同一块对内存)的时候,会产生数据混乱的情况。
  • 如何保证线程安全?
    1.对非安全的代码进行加锁控制;
    2.使用线程安全的类;
    3.多线程并发情况下,线程共享的变量改为方法级的局部变量
  • synchronized和Lock的使用、区别及底层实现
    使用:都是对程序进行加锁,用来同步化代码块,保证线程安全
    区别:
    synchronized:
    1.加锁不可以中断
    2.synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
    lock:
    1.首先他是一个接口,可以中断
    2.需要显示指定起始位置和终止位置。
    3.一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。
    4.在加锁和解锁处需要通过lock()和unlock()显示指出。
    5.一般会在finally块中写unlock()以防死锁。
    底层实现:锁的本质,是对象内存堆中头部的一部分数据。当线程获得一个锁,即是在锁内存区域设置一些标志。线程释放锁也是改变这些标记。**
  • volatile的原理和使用方式
    原理:volatile保证了新值能立即同步到主内存,并且通知其他cpu核心,你们缓存中的数据无效了,这样所有cpu核心再想对该volatile变量操作首先会从主内存中重新拉取值,从而保证数据安全
    使用方式:volatile只保证了程序的可见性,但是不具备原子性。使用volatile必须满足下面两个条件:
    1.对变量的写操作不依赖于当前值
    2.该变量没有包含在具有其他变量的不变式中。
    **适用场景举例:
    VolatileSimpleUse 不是线程安全的,因为get和set方法都是在没有同步的情况下进行的。如果线程1调用了set方法,那么正在调用的get的线程2可能会看到更新后的value值,也可能看不到。

public class VolatileSimpleUse {
    private int value;
    public int get(){
        return value;
    }
    public void set(int value){
        this.value = value;
    }
}

解决方法很简单,将value声明为volatile变量:

private volatile int value;

参考:https://www.cnblogs.com/krcys/p/9385360.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容