java内存模型与线程

目录

合理利用CPU

CPU高速缓存

CPU的执行非常快,而I/O操作很耗时,为了合理利用CPU所以现在计算机都能加入了高速缓存作为内存与处理器之间的缓冲。将需要运算的数据复制到高速缓存中,运算结束再将结果同步到内存中。

代码乱序优化

CPU可能会对代码乱序执行优化,保证最终结果是一致的。所以顺序不能靠代码的先后来保证。

java内存模型

java内存模型

java内存模型 规定所有变量都存在主内存中,每条线程还有自己的工作线程。
线程的工作内存保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须在工作内存中进行。不同线程无法直接访问对方的工作内存。
一个变量从内存拷贝到工作内存,再从工作内存同步到主内存的所有操作有8种。每一种都是原子的(对于long和double来说可能会有例外)
lock(锁定),unlock(解锁),read(读取),load(载入),use(使用),assign(赋值),store(存储),write(写入);从内存赋值到工作内存需要read和load操作,从工作内存同步到主内存需要store和write操作。

volatile关键字

可见性:一个线程修改了volatile变量的值,新值对于其他线程可以立即得知,也就是volatile变量的值对所有线程是一致的(因为每次只使用之前都会刷新)。但是并非线程安全的,因为java的运算并非原子操作。volatile变量的运算在并发下并不是线程安全的。

long和double的非原子协定

long和double是64位的数据类型,java虚拟机规范允许虚拟机将没有volatile修饰的64位数据类型划分为2个32位的操作进行。可能出现赋值到一半别的线程读取到既不是原始值又不是新值的情况,目前主流的虚拟机都把64位操作当成原子操作,所以这种情况基本不会出现。

原子性、可见性、有序性

  • 原子性:基本类型的访问读写都是原子性的。且synchronized块之间的操作也具备原子性。
  • 可见性:当一个线程修改了共享变量的值,其他线程能立即得知这个值的修改。java是通过变量修改后立即同步到主内存,在变量读取前从主内存刷新变量值,以主内存为传播媒介的方式实现可见性。
  • 有序性:指令重排序 和 工作内存与主内存的同步延迟,会造成无序。volatile和synchronized可以保证线程间的有序性。volatile 禁止指令重排序,synchronized一个变量同一时刻只允许一条线程对其进行lock操作。这个规则决定了持有同一个锁的两个同步块必须串行执行。

线程安全

互斥同步是一种常见的并发正确性保证手段,保证共享数据同一时刻只被同一个线程使用。
synchronized和ReetrantLock。ReetrantLock增加了一些高级功能:等待可中断,可实现公平锁,锁可以绑定多个条件。
非阻塞同步是通过原子性的CAS指令(比较后赋值,若不是预期的值就再次尝试);

自旋锁 自适应自旋

自旋锁,互斥同步是会阻塞线程,而共享数据的锁定状态大部分时候只会锁定很短的时间,为了这段时间去挂起和恢复线程并不值得。所以当两个线程并行请求的时候,就让后面那个线程”等一下”,但是不放弃CPU执行时间,为了让线程等待就让线程执行一个忙循环(自旋),自旋默认10次,超过次数还没有获得锁就挂起线程。
自适应自旋,根据上一次在同一个锁上的自旋时间来,调整自旋时间。如果上一次自旋成功过,那么这一次就等的久一点。如果以前没有成功过,就不再自旋。以免浪费CPU资源。

锁消除

虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。这些锁有些时候并不是程序员自己添加的,比如

// 这行代码表面上没有加锁
String s = s1 + s2 + s3;
// 编译后就会变成,append方法是加锁的,但这里的锁没有必要
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3);

这种锁确认变量不会逃逸,虽然这里有锁但是可以安全的被擦除。

锁粗化

以上面的代码为例 append 方法会反复的对一个锁加锁和解锁,造成不必要的性能损耗;这时候就可以进行锁粗化,将锁扩展到第一个append之前知道最后一个append之后。这样只需要加锁一次就好了。

轻量级锁 重量级锁

轻量级锁是在没有多线程竞争的前提下,减少传统重量级锁使用操作系统互斥量产生的性能消耗;
当只有2个线程争锁的情况下有效,如果有两条以上的线程争锁的情况就必须膨胀为重量级锁,后面的线程也要进入阻塞状态。释放锁的时候会去唤醒挂起的线程。

偏向锁

当锁对象第一次被线程获取的时候虚拟机标志位设为偏向模式,同时记录下这个线程的id,持有偏向锁的线程每次进入这个锁相关的同步块时,都不会进行任何同步操作。当有另外一个线程取获取锁的时候偏向模式就结束,根据目前是否处于被锁定的状态,撤销偏向后恢复到未锁定或者轻量锁。如果程序中大多数锁总是被不同线程访问那偏向模式就是多余的,可以关闭以提高性能。
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值