并发编程——共享对象的正确使用

共享对象的正确使用

概述

​ 在JMM,Java内存模型中讲过,其实并发编程的问题主要在多线程对共享变量的修改读取上,那么互斥(加锁),其实是通过同步来避免多个线程在同一时刻访问共享变量,但是其实并不是所有的场景都需要加锁。比如并不是所有的场景都会更改共享变量的值,它们仅仅需要把主内存的数据读取出来,然后在工作内存中拷贝一份来读取(线程本地存储);也不是所有的场景都要求立马读取最新的数据(cop-on-write);甚至有些场景直接不共享变量的值—线程封闭;又或者这个变量的值永远都不会改变—不变模式。

​ 本文首先会介绍如何正确的封装与发布共享变量,然后会按照不同的需求推荐并发访问策略。

​ 锁会在后面重点描述,所以本文略过

在这里插入图片描述


封装共享变量

​ 如果从面向对象的思想来考虑并发编程的安全性,其实就是将共享变量作为对象属性封装在内部,然后对所有公共的方法指定并发访问策略。但是这就涉及到两个问题——在并发编程下,保证对象的可见性与正确的发布对象,防止对象逸出。


对象的可见性

​ 这里指的可见性与JMM中提到的可见性其实是一个意思。在多个线程同时访问一个共享变量的情况下,可见性的问题尤为重要:

​ 试想一下,i++的例子中,线程1把i由0改成了1,线程2同时也把i由0改成了1,如果变量i没有任何同步措施保证,那么线程1跟2同时把工作内存中的i回写到主内存,那么主内存中的i最终值就是1,因为线程1跟线程2中各自工作内存中变量i的值互相不可见。

​ 保证对象可见性有很多种方案,根据JMM的Happends-Before原则,可以通过加锁与volatile两种方式。

加锁

​ 加锁并不仅仅局限于互斥行为,它也包括内存可见性。

​ 当线程A执行到某个同步代码块,线程B随后进入到同一个锁保护的同步代码块,那么根据Happens-Before原则,线程A释放锁后,线程B获得了锁,那么线程A对同步代码块中所有的操作结果对线程B都是可见的。

volatile

​ volatile是Java提供的一种稍弱的同步机制。它主要可以做两件事:确保将变量的更新操作通知到其他线程;不会将该变量上的操作与其他内存操作一起重排序。简单的说,就是可见性与防止指令重排。volatile变量不会被缓存到寄存器或者其他对处理器不可见的地方,因此读取volatile类型的变量时总会返回最新的值。

volatile原理

“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”——《深入理解Java虚拟机》

​ lock前缀指令相当于一个内存屏障,主要做了这几件事:

  • ​ lock操作相当于一个写屏障,指令重排的时候不能把后面的指令重排到内存屏障之前的位置

  • ​ lock前缀使得本CPU的cache写入了缓存,相当于会立刻把工作内存中这个变量的值回写到主内存

  • ​ 该写入动作也会引起别的CPU或者别的内核的缓存无效,强制别的线程的工作内存中,这个变量的缓存无效,要求别的线程立刻从主内存读取这个变量的值

    通过这个操作也就可以保证volatile变量的修改对其他CPU立即可见

volatile的正确使用

​ 加锁即可以保证可见性,又可以保证原子性,但是volatile变量仅仅可以保证可见性

​ 只有在这几种条件下,才应该使用volatile变量:

  • ​ 对变量的写入操作不依赖变量的当前值,或者确保只有单个线程更新变量的值(比如volatile修饰变量i,但是对变量i做++操作并不是线程安全的)
  • ​ 该变量不会与其他状态状态变量一起纳入不变条件
  • ​ 在访问该变量时不需要加锁

对象的发布与逸出

发布:使对象能够在当前作用域之外的代码中使用

逸出:当某个不应该发布的对象被发布时,就称为逸出

逸出的几种情况:
  • ​ 方法返回一个private对象

  • ​ 还未完成初始化就把对象提供给外部:

    • 在构造函数中未初始化完毕就this赋值

    • 隐式逸出——注册监听事件

    • 构造函数中运行线程

逸出举例
举栗一:

​ 方法返回一个private对象

public class Example1 {
   
    public static void main(String[] args) {
   
        UnSafeArr unSafeArr=new UnSafeArr();
        String[] arr &
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值