• 执行结果如下表

次数(count)
1w
10w
100w
1000w 1亿

串行耗时(ms)

2
5
<10
53
466

并发耗时(ms)

2
10
10-12
25
166
  • 没有运行java程序前的cs变化

并发面临的问题小结-LMLPHP

  • 执行java程序之后的cs变化

并发面临的问题小结-LMLPHP

  • 结果分析

  • 1) 在count数据不是特别多的情况,串行执行的效率比并发快,因为并发执行需要切换线程上下文

    2) 随着次数的增加,串行执行的效率比并发执行效率低,原因是当前线程充分利用CPU核数的资源,利用多个线程在相应的CPU上执行,使得任务被对应的线程消费,在这种情况下,并发线程充分利用CPU空闲的资源完成任务的调度


  • 临界区: 在并发多线程中执行一系列对共享资源的修改操作的代码区域,在该区域下的操作的执行结果会对其他线程产生影响,称该代码区域为临界区

  • 竞态条件: 表示并发多线程执行产生临界区的必要条件,也就是在临界区存在数据竞争,而数据竞争主要条件就是来源于多线程需要对共享资源执行读写操作,简言之就是多线程争夺共享资源的使用

  • 代码示例

  • 对程序代码指令而言,表示一个步骤;对整体业务逻辑而言,表示一系列步骤并且这一系列步骤在整体业务逻辑中一个是不可被中断, 与其他的业务代码步骤是不可被重排序的

  • 核心特征就是相对整体业务逻辑而言,该一系列步骤要么全部成功,要么全部失败,在整体的业务逻辑保持操作结果的一致性

  • 需要原子性操作的原因,在并发多线程中存在竞态条件,临界区的执行结果会对其他线程产生影响,如果不能保证所有线程看到的操作都是一致的(要么成功然后处理成功的逻辑,要么失败然后处理失败的逻辑),这样才能够保证我们的实际应用业务是可控的,结果是可预期的

  • 非共享资源是属于线程安全的

  • 1) 在当前线程栈中的局部变量.方法参数,抛出异常的处理器对象,由于只在线程栈中自己使用,并没有共享给其他线程,因此这类数据是属于线程安全的,也就是不存在数据竞争的情况

    2) ThreadLocal以及ThreadLocalRandom等存储的数据变量

  • 通过加锁技术方案

  • 不可变的变量数据,即使用final修饰的变量数据



  • 产生原因

    多线程相互争抢对方相互持有的资源,由于获取不到资源一直处于挂起状态而无法继续往下执行

  • 死锁示例伪代码

  • 解决方案

1) 使用tryLock(timeout)的方式,一旦超时将自动释放锁资源

2) 可以考虑在不影响结果的情况下调整程序指定的逻辑分先后执行

3) 其他方案: 在业务代码中如果能够使用单锁解决问题则使用单锁的方式


  • 机器资源的限制

1) 硬件方面有CPU核数以及CPU的处理读写能力, 网络带宽问题, 磁盘读写速度, 磁盘空间, 内存空间等因素;

2) 软件资源一般是并发线程池的数量,比如tomcat服务的并发线程数, 数据库连接池大小, 网络socket连接数等

  • 资源导致的问题

1) 如果机器的CPU核数较少,比如只有一个的话,在机器启动jvm进程来创建多线程会容易导致线程切换频繁,再加上本身线程切换存在资源调度的性能消耗,容易降低程序执行效率

2) 内存空间不足也会导致创建并发线程个数受限,同时容易造成OOM的错误

3) 业务处理线程数多于数据库连接池数,如果数据库中的sql执行比较快的话,那么会导致程序很多业务进程处于阻塞等待状态,容易引起100%的CPU

  • 常规解决思路

1) 提前对开发的应用做好并发量的评估,通过压力测试每台机器每个JVM进程在单位时间所能承担的并发量,然后根据预估计算需要分配的资源,比如网络带宽,JVM启动的内存分配,实际的机器个数等

2) 根据业务的读写场景,对文件并发读写频繁的业务可以选择IO磁盘处理能力较强的机器,网络并发读写较强的业务需要考虑带宽以及可以动态分配的机器最大个数,以便扩容需要

3) 在实际应用中,开启线程的个数可以默认设置为系统CPU核数*2+1



并发面临的问题小结-LMLPHP

老铁们关注走一走,不迷路


本文分享自微信公众号 - 疾风先生(Gale2Writing)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

09-10 11:30
查看更多