Java并发的场景&原因&问题浅谈

并发的三种场景

分工

分工是多线程并发最基本的场景,各司其职,完成各自的工作。分工,就是线程各司其职,完成不同的工作。分工,也是有很多模式的。比如有:

  • 生产者-消费者模式;
  • MapReduce模式,把工作拆分成多份,多个线程共同完成后,再组合结果,Java8中的stream与Fork/Join就是这种模式的体现;
  • Thread-Per-Message模式,服务端就是这种模式,收到消息给不同的Thread进行处理

同步

有分工就要有同步,不同工人之间要协作,不同线程也是。一个线程的执行条件往往依赖于另一线程的执行结果。

线程之间最基本的通信机制是管程模式与wait/notify,除此外还有多个工具类,如:

  • Future及其衍生的工具类FutureTask/CompletableFuture等,可以完成异步编程;
  • CountDownLatch/CyclicBarrier可以实现特定场景的协作;
  • Semaphore提供了经典的PV同步原语,还可以作为限流器使用;
  • ReentrantLock与Condtion,对管程同步的扩展;

互斥

多线程访问相同的共享变量,就需要做互斥处理。分工与协作强调的是性能,互斥问题强调的是正确,即线程安全问题。Java解决互斥问题提供了很多思路与工具。

  • 避免共享,没有共享,没有竞态,就没有伤害,如ThreadLocal;
  • 没有改变,如果大家都不做改变,都是只读的,一起也没有错;
  • Copy-on-write,你变你的,我变我的,每变一次都生成新的副本,只要不冲突就可以并行;
  • CAS,写入前要看一看,有没有物逝人非(变量和自己读取时一样),没有再写入,否则再做一变;
  • Lock,最终手段,但也不想做得太绝,够用就行,ReadWriteLock/StampedLock,够用就行

并发问题产生的原因

缓存导致的可见性问题

在运行时,同一份数据就出现了两份,一个在内存,一个在CPU缓存。每个CPU中有各自的数据缓存(JMM内存模型)。

线程切换带来的原子性问题

计算机看起来可以同时运行多于自身核数的线程,是因为现代操作系统的分时切换机制。分时机制提高了CPU的使用率,也可以保证多线程可以相对公平地获取CPU。但分时机制导致了一个不可避免的问题,就是线程切换。发生线程切换时,被休眠的线程会暂存现场,包括PC(程序计数器)与栈等。等到此线程再次被唤醒,可能发现这个世界已经物是人非了,因为一条高级语言指令可能对应多条CPU指令。

编译优化带来的有序性问题

JAVA为了优化性能,可能对指令进行重排,这些重排在大部分时候是无害的。但是有些时候,可能导致意想不到的Bug。由重排引起的一个经典问题是双重量检查创建单例。


并发的三种问题

安全性问题

并发程序因为可见性、原子性及有序性问题等导致的正确性问题

活跃性问题

指的是某个操作无法执行下去,如死锁等导致的问题

性能问题

一般都是由锁的滥用引起的。
性能方面有三个主要的指标:吞吐、时延及并发量。

  • 吞吐,指单位时间处理的请求数;
  • 时延,指单次处理的平均耗时;
  • 并发,同一时刻可以接入的请求数
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值