美文网首页
一、多线程安全性问题

一、多线程安全性问题

作者: kar_joe | 来源:发表于2019-12-28 15:12 被阅读0次

本系列文章是极客时间课程的读书总结。

背景

cpu、内存、IO速度差异过大,如何均衡速度,提高系统综合性能?

  • 多核,硬件并行
  • 多线程,分时复用cpu,提高IO以及cpu综合利用率
  • 缓存,平衡cpu与内存速度差异
  • 编译器/执行器优化指令顺序,更充分利用缓存
    通过以上举措,可以较大提高系统性能,但是万物皆有利弊,也带来了线程安全性问题,如果没有解决该问题,不仅不会提升性能,还会导致程序运行异常。

线程安全性定义

在多线程访问时,程序依旧表现正常,符合预期

线程安全性问题分类

线程安全性问题出现场景:多线程同时读写共享的、可变的数据。
问题主要有一下几类:

  1. 原子性问题
    原子性操作定义:一个或多个操作在CPU执行过程中无法被中断的特性
    问题原因:多线程并发
    示例:count+=1;long/double类型变量读写
    解决办法:临界区加锁保护,保证单线程访问,其他线程阻塞等待,保证中间状态不可见
  2. 可见性问题


    1.png

    可见性问题定义:线程A对共享数据的修改,是否能立即被线程B看到的问题
    问题原因:缓存
    解决办法:按需禁用缓存

  3. 有序性问题
    问题原因:编译器/执行器为优化性能,提高对缓存的利用,会在不改变单线程执行结果的情况下调整执行顺序
    解决办法:按需禁用编译器执行器优化
    举例:
public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}

理想顺序:开辟内存->初始化->赋值引用;
优化后顺序:开辟内存->赋值引用->初始化
解决办法:instance改为volatile变量

  1. 举例
class Example {
  int x = 0;
  boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }
  public void reader() {
    if (v == true) {
      // 这里 x 会是多少呢?
    }
  }
}

Java提供哪些能力解决多线程安全性问题

volatile、synchronized 和final关键字,以及六项 Happens-Before 规则

  1. final
    生而常量,随便优化,注意逸出问题
  2. volatile
    保证可见性、有序性,不保证原子性
    volatile禁用缓存以及编译优化,强制刷内存(volatile变量本身以及之前的其他数据更新都强制刷内存);另有一条heppen规则加强volatile语义(volatile变量写happen-before读)
int a = 1;
int b = 2;
volatile int c = 3;
int d = 4;
int e = 5;
  1. synchronized
    解决可见性、有序性、原子性问题
    临界区加锁保护,保证单线程访问,其他线程阻塞等待,保证中间状态不可见
//注意组合操作,要加锁
class Account {
  private int balance;
  // 转账
  void transfer(
      Account target, int amt){
    if (this.balance > amt) {
      this.balance -= amt;
      target.balance += amt;
    }
  }
}
class Account {
  private int balance;
  // 转账
  synchronized void transfer(
      Account target, int amt){
    if (this.balance > amt) {
      this.balance -= amt;
      target.balance += amt;
    }
  }
}
//是否保证了线程安全?

等待-通知模型


2.png

wait与sleep区别在于:

  • wait会释放所有锁而sleep不会释放锁资源.
  • wait只能在同步方法和同步块中使用,而sleep任何地方都可以.
  • wait无需捕捉异常,而sleep需要.
  • sleep是Thread的方法,而wait是Object类的方法;
  • sleep方法调用的时候必须指定时间
class Allocator {
  private List<Object> als;
  // 一次性申请所有资源
  synchronized void apply(
    Object from, Object to){
    // 经典写法
    while(als.contains(from) ||
         als.contains(to)){
      try{
        wait();
      }catch(Exception e){
      }   
    }
    als.add(from);
    als.add(to);  
  }
  // 归还资源
  synchronized void free(
    Object from, Object to){
    als.remove(from);
    als.remove(to);
    notifyAll();
  }
}
  1. 六项Happen-before原则
    A happen-before B,表示A操作对于B内存可见
    1. 顺序性规则
      单线程中,前面操作对后面操作可见,特别注意,不是表面上的代码顺序,是根据内存模型约束下编译器优化后的执行顺序
    2. volatile 变量规则
      一个 volatile 变量的写操作, Happens-Before于该变量的读操作
    3. 传递性
      A Happens-Before B,且B Happens-Before C,则A Happens-Before C
class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }
  public void reader() {
    if (v == true) {
      // 这里 x 会是多少呢?
    }
  }
}
  1. 锁规则
    一个锁的解锁 Happens-Before 于后续对这个锁的加锁操作
  2. 线程 start() 规则
    线程A启动线程B之后,线程B能看到之前线程A做的所有操作
  3. 线程 join() 规则
    线程A等待线程B执行完成后,线程B的所有操作的对线程A可见

相关文章

  • 02.线程安全性问题

    [TOC] 安全性问题概述 什么是安全性问题 多线程情况下的安全问题,是指数据的一致性问题,在多线程环境下,多个线...

  • 多线程安全性和Java中的锁

    Java是天生的并发语言。多线程在带来更高效率的同时,又带来了数据安全性问题。一般我们将多线程的数据安全性问题分为...

  • ThreadLocal实现原理揭秘

    ThreadLocal是什么?对java多线程有了解的人都清楚,在多个线程程序中,会出现线程安全性问题,即多线程是...

  • 一、多线程安全性问题

    本系列文章是极客时间课程的读书总结。 背景 cpu、内存、IO速度差异过大,如何均衡速度,提高系统综合性能? 多核...

  • 聊聊高并发(二)结合实例说说线程封闭和背后的设计思想

    高并发问题抛去架构层面的问题,落实到代码层面就是多线程的问题。多线程的问题主要是线程安全的问题(其他还有活跃性问题...

  • 多线程与线程安全

    多线程核心问题 多线程要解决的核心问题包括三个,分别是原子性问题,可见性问题,有序性问题 原子性 即一个操作或者多...

  • java多线程安全性问题

    线程安全性的概念 当多个线程访问某个类时,不管运行时环境才去何种调用方式,这些线程如何交替执行,并且在主调代码中不...

  • 多线程如何实现同步-多线程之间通讯

    一. 什么是线程安全问题 多线程同时对同一个全局变量做写的操作,可能会受到其他 线程的干扰,就会发生线程安全性问题...

  • [Java多线程编程之九] 线程安全

      并发编程的东西理论太多不太好理解,直接上代码。 一、线程安全的原子性问题   在多线程环境中,很多时候我们希望...

  • 并发编程与锁的底层原理,看完稳了!

    背景: 并发编程,多核、多线程的情况下,线程安全性问题都是一个无法回避的难题。虽然我们可以用到CAS,互斥锁,消息...

网友评论

      本文标题:一、多线程安全性问题

      本文链接:https://www.haomeiwen.com/subject/etjsoctx.html