海量数据处理:从并发编程到分布式系统

本系列文章主要围绕高并发这一话题展开,分享笔者在并发处理上的学习思路以及踩过的坑。具体思路大体分为三部分:

  • Java多线程编程;
  • 高并发的解决思路;
  • 分布式架构中Redis、Zookeeper分布式锁的应用。

本文将重点讲解第一部分——Java多线程编程。

一、Java内存模型与线程

并发编程主要讨论以下几点:

  • 多个线程操作相同资源
  • 保证线程安全
  • 合理使用资源

通常我们可以将物理计算机中出现的并发问题类比到JVM中的并发。

物理计算机处理器、高速缓存、主内存间交互关系如图:

 

处理器为提高性能,会对输入代码乱序执行(Out-Of-Order Execution) 优化。

类比Java内存模型,线程、主内存、工作内存交互关系如图:

 

JMM定义了程序中各个变量访问规则,即在虚拟机中将内存取出和存储的底层细节。

 

 

线程A如果要跟线程B要通信的话,必须经历以下两个步骤:

  • 线程A把本地内存A中更新过的共享变量的值刷新到主内存中;
  • 线程B去主内存中读取A更新过的共享变量的值。

线程的工作内存中保存了该线程使用到变量的主内存副本拷贝(也可理解为此线程的私有拷贝),线程对变量的操作(读取、赋值等)都在工作内存中进行,而不能直接读写主内存中变量。不同线程之间的通信也需要通过主内存来完成。主内存对应Java堆中对象实例数据部分,而工作内存则对应虚拟机栈中部分区域。

在此还有非常重要的点需要提及!

指令重排序

执行程序时,为提高性能,编译器和处理器常常会对指令做出重排序。分三种:

  • 编译器优化的重排序;
  • 指令并行重排序;
  • 内存系统重排序。

 

内存之间的交互操作

JMM中定义了8种操作来描述工作内存与主内存之间的实现细节:

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占状态;
  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
  • read(读取):作用于主内存的变量,它把一个变量从主内存传输到线程工作内存中,以便后边的load操作;
  • load(载入):作用于主内存的变量,它把read操作从主内存中得到的变量值放到工作内存副本中;
  • use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时,将会执行这个操作;
  • assign(赋值):作用于工作内存的变量,它把从执行引擎接收到的值赋给工作内存,每当虚拟机遇到一个给变量赋值的字节码指令时执行此操作;
  • store(存储):作用于工作内存的变量,它把工作内存的变量的值传送到主内存中,以便以后的write操作使用;
  • write(写入):作用于主内存的变量,它把store操纵从工作内存中得到的变量值放入到主内存的变量中。

JMM规定了执行上述八种操作时必须满足的规则(与happens-before原则是等效的,即先行发生原则):

  • 不允许read和load、store和write操作之一单独出现;
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中;
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中;
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作;
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现;
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值;
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量;
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)

测试工具

二、线程安全性

原子性:提供了互斥访问,同一时刻只能由一个线程来对它进行操作。

可见性:一个线程对主内存的修改可以及时被其他线程观察到。

有序性:一个线程观察其它线程中指令执行顺序,由于指令重排序的存在,观察的结果一般为杂乱无章的。Java程序的天然有序性可以总结为——如果本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。前者指的是线程内的串行语义,后者指的是指令重排序和工作内存和主内存同步延迟现象。

1、原子性-Atomic包

AtomicXXX:

CAS、Unsafe.compareAndSwapInt

 

 

 

通过CAS来保证原子性,即Compare And Swap比较交换:

CAS虽然可以进行高效的进行原子操作,但是CAS仍在存在三大问题:

  • ABA问题。在Java1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。大部分情况下ABA问题并不影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更加高效;如果你对大数据开发感兴趣,想系统学习大数据的话,可以加入大数据技术学习交流扣扣君羊:522189307
  • 循环时间长开销大;
  • 只能保证一个共享变量进行的原子操作。

测试:

public class AtomicExample1 {

// 请求总数

public static int clientTotal = 5000;</

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值