乐观锁与悲观锁
锁是用来做并发最简单的方式,当然其代价也是最高的。内核态的锁的时候需要操作系统进行一次上下文切换,加锁、释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放。在上下文切换的时候,cpu之前缓存的指令和数据都将失效,对性能有很大的损失。用户态的锁虽然避免了这些问题,但是其实它们只是在没有真实的竞争时才有效。
Java在JDK1.5之前都是靠synchronized关键字保证同步的。
独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
java内的原子性操作
- 所有基本类型的赋值,除了long和double
- 所有引用的赋值
- java.concurrent的所有操作。 原子*类
- 所有赋值给volatile long和double
CAS(比较并交换)是CPU指令级的操作,只有一步原子操作,非常快。
volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性。
不要将volatile用在getAndOperate场合,仅仅set或者get的场景是适合volatile的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0RZuyg3p-1645971733602)(E:\note\image\volatile.png)]
load到store之间是不安全的
volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。写入字段时所有线程都可以获得2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。强制刷新一次缓存
不能用于构建原子的复合操作,因此当一个变量依赖旧值时就不能使用volatile变量。
特别地,long型赋值不是原子操作,用volatile修饰后long型赋值可变为原子操作。
服务器编程三大性能杀手
- 大量线程导致的线程切换开销。
- 锁。
- 非必要的内存拷贝
大量线程导致的线程切换开销。
2. 锁。
3. 非必要的内存拷贝
垃圾收集器使用非阻塞算法加快并发和平行的垃圾搜集;调度器使用非阻塞算法有效地调度线程和进程,实现内在锁。