2

并发编程总结

 2 years ago
source link: https://yang295513.github.io/2020/04/15/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E6%80%BB%E7%BB%93/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这篇文章只会梳理一下知识,并不会详细讲解每个知识点

上下文切换

什么是上下文切换

CPU通过实践片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务的从保存到再加载的过程就是一次上下文切换。

如何减少上下文切换

  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以我们可以避免使用锁,例如使用hash算法隔离不同线程的数据。
  • 使用CAS算法,例如java的Atomic包的CAS算法来更新数据,就不需要加锁
  • 使用最少线程。避免创建不需要的线程。
  • 合理使用volatile变量实现多线程,volatile变量比synchronized的使用成本和执行成本更低,而且也不会引起线程的上下文切换和调度。
  • 协程,在单线程里实现多任务的调度,并在单线程里面维持多个任务间的切换。

为什么单线程不一定比多线程慢

在多线程的情况下,由于线程间的争抢,难免引入锁的机制,所以相比较单线程由于存在上下文切换,所以以至于单线程不一定比多线程慢。

在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但是如果将某段代码串行改成并发执行,由于受限于资源仍在串行执行,这个时候多线程不仅不会变快,反而会变慢

并发中关注的两个问题

线程之间如何通信和线程之间如何同步

线程之间的通信机制有两种

共享内存和消息传递

如何避免死锁

  • 避免一个线程同时获取多个锁

  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源

  • 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制

  • 对于数据库锁,必须保证加锁和解锁必须在一个数据库连接里面,否则会出现解锁失败的情况

volatile

volatile是轻量级的synchronizd,它可以保证共享变量的可见性,但不能保证该变量的原子性,并且volatile不会引起线程的上下文切换

》 volatile

java语言规范第3版对于volatile的定义如下:

java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排它锁单独获取这个变量。

为什么能保证可见性呢

凡是被volatile修饰的变量,要求每个线程在取的时候都必须到主内存中去取,写也要强制写入到主内存中。

JAVA内存模型

为什么要进行指令重排序?

在执行程序时,为了提高性能,编译期和处理器常常会对指令做重排序。

例如(只是大概演示重排序的作用,实际上可能并不是如此)

x=0;
y=0;
x1=y;
y1=x;

有如上代码如果没有指令重排序的话需要花费4条指令的时间

如果进行了重排序可以同时让两个处理器执行

CPU1 CPU2
x=0 y=0
x1=y y1=x

那么就可以不影响结果的情况下花费两个指令的时间。

指令重排序的种类

  • 编译期优化的重排序
  • 指令集并行的重排序,现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应及其指令的执行顺序
  • 内存系统的重排序

源代码—>编译期优化重排序—>指令级并行重排序—->内存系统重排序—>最终执行的指令

happens-before简介

在JSR-133中使用happens-before的概念来阐述操作之间的内存可见性,如果一个操作执行的结构需要对另一个操作可见,那么这两个操作必须满足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。

两个操作之间具有happens-before关系,并不意味着前一个操作一定在后一个操作之前!。而是前一个操作的结果对于后一个操作可见。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK