线程安全和对应的核心概念

线程安全

  • 线程安全的概念:当多个线程访问某一个类(对象和方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或者方法)就是线程安全的
  • synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为‘互斥区’或者“临界区”
  • 当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的),一个线程想要实现synchronized装饰的方法里的代码,首先是尝试获取锁,如果可以得到锁,就可以执行synchronized代码体的内容;如果拿不到锁,这个线程就会不断尝试去获取锁资源,直到拿到为止;而且是多个线程同时去竞争这把锁(锁竞争)

Synchronized

  • 同步Synchronized:同步的概念就是共享,如果不是共享的资源就没有必要进行同步
  • 异步asynchronized:异步的概念就是独立,相互之间不受到任何制约
  • 同步的目的是为了保证线程的安全,对于线程安全需要保证两个特性 原子性(同步)和可见性

线程之间通信

  • 线程通信的概念:线程是操作系统中独立的个体,但是这些个体如果不经过特殊的处理就不能成为一个整体,线程之间的通信就成为整体的必用的方式之一。当线程之间存在通信指挥,系统之间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对于线程任务在处理过程中,进行有效的把控和监督
  • 使用wait/notify方法实现线程之间的通信(这两个方法都是object的类的方法,即java为所有的对象都提供了这两个方法)
  • wait和notify必须配合synchronized关键字使用
  • wait方法释放锁/notify方法不释放锁

ThreadLocal概念

  • 线程局部变量,是一种多线程间并发访问变量的解决方案,与synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程的安全
  • 从性能上说,ThreadLocal不具有绝对的优势,在并发不是很高的情况下,加锁的性能可能会更好,但是作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争

Volatile关键字核心概念与应用

  • Volatile:主要作用是使变量在多个线程之间可见
  • 阻止指令的重排序,happens-before
  • 一个线程可以执行的操作有使用(use)、赋值(assign)、转载(load)、存储(store)、锁定(lock)、解锁(unlock)
  • 主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的
  • volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不是去线程工作内存里去读取,从而实现了多个线程之间变量的可见,也就是满足线程安全的可见行

JVM

  • Java Memory Model(Java内存模型),简称JVM,用于解决线程对于共享变量的写入何时对于另一个线程可见
  • 所有变量都存储在主内存中,每一个线程都有一个私有的本地内存,本地内存是将该线程使用到的变量,从主内存中拷贝到本地
  • 线程对于变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量也不例外)
  • happens-before规则,例如操作A :i=1;操作B:j=i;如果操作Ahappens-before操作B,那么操作B完成之后,j的值一定为1;
  • 因为happens-before关系可以向程序员保证,在操作B执行之前,操作A的执行后的影响【或者说明结果】(修改i的值)对于操作B是可以观察到的【可见的】

规则

  • 简而言之,使用happens-before概念来阐述操作之间的内存可见行
  • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作,也就是说,你写的操作,如果是单线程执行,那么前面的操作就会happens-before于后面的操作
  • 监视器锁规则:对于一个锁的解锁,happens-before于随后对这个锁的加锁
  • Volatile变量规则:对于一个volatile域的写,happens-before于任意后续对这个volatile域的读
  • 传递性规则:A happens-before B,B happens-before C,则A happens-before C

指令重排序

  • 为了保证程序的最终运行结果需要和在单线程严格意义的顺序化环境下执行的结果一致,程序指令的执行顺序有可能和代码的顺序不一致,这个过程就称之为指令的重排序
  • 指令的重排序的意义:JVM利用处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,从而可以充分利用CPU的执行特点,最大程度上发挥机器的性能

Atomic系列类比

  • Atomic系列类封装了一系列的基础类型和对象操作,其目的是为了实现原子性
  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReference
  • 注意:在对Atomic类操作的时候,如果有多个操作执行,那么就是非原子性的,需要加aynchronized进行修饰,保证Atomic类操作的整体原子性
发布了53 篇原创文章 · 获赞 1 · 访问量 1163

猜你喜欢

转载自blog.csdn.net/CHYabc123456hh/article/details/104443784