《Java并发编程实战》课程学习笔记(五)

安全性、活跃性以及性能问题

  • 并发编程中我们需要注意的问题有很多,很庆幸前人已经帮我们总结过了,主要有三个方面,分别是:安全性问题活跃性问题性能问题

安全性问题

  • 那什么是线程安全呢?其实本质上就是正确性,而正确性的含义就是程序按照我们期望的执行,不要让我们感到意外。
  • 存在共享数据并且该数据会发生变化,通俗地讲就是有多个线程会同时读写同一数据。
    • 那如果能够做到不共享数据或者数据状态不发生变化,不就能够保证线程的安全性了。
    • 有不少技术方案都是基于这个理论的,例如线程本地存储(Thread Local Storage,TLS)、不变模式等等。
  • 当多个线程同时访问同一数据,并且至少有一个线程会写这个数据的时候,如果我们不采取防护措施,那么就会导致并发 Bug,对此还有一个专业的术语,叫做数据竞争(Data Race)。
  • 所谓竞态条件,指的是程序的执行结果依赖线程执行的顺序。
  • 那面对数据竞争和竞态条件问题,该如何保证线程的安全性呢?
    • 其实这两类问题,都可以用互斥这个技术方案,而实现互斥的方案有很多,CPU 提供了相关的互斥指令,操作系统、编程语言也会提供相关的 API。
    • 从逻辑上来看,我们可以统一归为:锁。

活跃性问题

  • 所谓活跃性问题,指的是某个操作无法执行下去。
    • 我们常见的“死锁”就是⼀种典型的活跃性问题,当然除了死锁外,还有两种情况,分别是“活锁”和“饥饿”。
    • 有时线程虽然没有发生阻塞,但仍然会存在执行不下去的情况,这就是所谓的“活锁”。
      • 解决“活锁”的方案很简单,尝试等待一个随机的时间就可以了。
    • 所谓“饥饿”指的是线程因无法访问所需资源而无法执行下去的情况。
      • 解决“饥饿”问题的方案很简单,有三种方案:一是保证资源充足,二是公平地分配资源,三就是避免持有锁的线程长时间执行。
      • 在并发编程里,主要是使用公平锁。
      • 所谓公平锁,是一种先来后到的方案,线程的等待是有顺序的,排在等待队列前面的线程会优先获得资源。

性能问题

  • “锁”的过度使用可能导致串行化的范围过大,这样就不能够发挥多线程的优势了,而我们之所以使用多线程搞并发程
    序,为的就是提升性能。
  • Java SDK 并发包里之所以有那么多东西,有很大一部分原因就是要提升在某个特定领域的性能。
    • 第一,既然使用锁会带来性能问题,那最好的方案自然就是使用无锁的算法和数据结构了。
      • 在这这方有很多相关的技术,例如线程本地存储 (Thread Local Storage, TLS)、写入时复制 (Copy-on-write)、乐观锁等;
      • Java 并发包里面的原子类也是一种无锁的数据结构;
      • Disruptor 则是一个无锁的内存队列,性能都非常好。
    • 第二,减少锁持有的时间。
      • 互斥锁本质上是将并行的程序串行化,所以要增加并行度,⼀定要减少持有锁的时间。
      • 这个方案具体的实现技术也有很多,例如使用细粒度的锁,一个典型的例子就是 Java 并发包里的 ConcurrentHashMap,它使用了所谓分段锁的技术;
      • 还可以使用读写锁,也就是读是无锁的,只有写的时候才会互斥。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值