JVM笔记-高效并发(三)

1.内存模型与线程

8种内存模型基本操作:lock、unlock、read、load、assign、use、store、write

volatile关键字(重要)

可见性,一个线程修改这个变量的值,新值对于其他变量来说是可以立即得知的。

因为java运算不是原子操作,所以在并发下是不安全的,取值的时候能保证正确,但是在计算时,可能有其他线程把它修改了

禁止指令重排序优化,前面的指令全部完成了才能执行后面的指令,相当于一个屏障。

long和double类型变量的特殊规则

long和double是64位数据,可能会被划分两次32位的操作,如果未声明volatile可能会读取到被修改了一半的变量

但在商用虚拟机中把64位数据的读写作为原子操作,不需要特意声明volatile

Java和线程

实现线程的3种方式:

使用内核线程,内核线程(KLT)和轻量级进程(LWP)关系为1:1,有轻量级进程阻塞了也不会影响整个进程工作

局限性:操作需要系统调用,代价较高,需要消耗内核资源,能创建的数量有限

使用用户线程,操作快速低耗,支持大规模线程数量,进程与用户线程关系为1:N

劣势:没有内核支援,所有线程操作都要用户处理

使用用户线程加轻量级进程混合

优势:降低了整个进程被完全阻塞的风险

 

线程调度

协同式线程调度

相当于串行执行,当线程出现问题发生阻塞

抢占式线程调度

每个线程由系统来分配执行时间,不会因为一个线程导致整个进程阻塞。

优先级越高的线程越容易被系统选择执行,但不靠谱。

在不同系统下优先级表现不一样,尤其是分级比java线程优先级还少的系统。

 

线程状态转换

 

2.线程安全与锁优化

共享数据分为5类:

不可变,一定是线程安全的,如果不发生this逃逸

绝对线程安全,这类数据不需要作任何额外的同步措施,java中标注线程安全的类,大多数不是绝对线程安全

相对线程安全,单独操作对象是安全的,对于特定顺序的连续调用,需要额外同步代码,如Vector、HashTable

线程兼容,对象本身不是线程安全的,在调用端使用同步手段即可线程安全

线程对立,无论做了何种同步,都无法线程安全,如Thread类的suspend()和resume(),已废弃

 

线程安全实现方法

1.互斥同步,主要使用synchronized关键字,在1.6版本后,性能与ReentranLock差不多

synchronized和ReentranLock都是线程可重入的,

ReentranLock区别和高级功能:

①可中断,长期等不到锁可以放弃等待

②公平锁,按照申请锁的顺序获得锁,默认是非公平的,可通过带布尔值的构造函数要求使用公平锁

③绑定多个条件,绑定多个Condition实现

2.非阻塞同步,CAS操作,

会产生ABA问题,可以使用带有标记的原子引用类

但大部分情况下,如果要解决ABA问题,还不如直接互斥同步更高效

3.无同步方案,略

 

锁优化(重要)

自旋锁,互斥同步阻塞需要内核切换,消耗大,为了解决这个问题,

使当前线程执行一个忙循环,看看持有锁的线程是否会释放锁。自旋次数默认10次

自适应自旋锁,根据前一次在同一个锁上的自旋时间及锁拥有者的状态决定自旋时间长短。

锁消除,虚拟机在编译时,把不可能存在数据竞争的锁进行消除。

锁粗化,原则上锁的粒度越小越好,但是如果对同一个对象反复加锁解锁,那么虚拟机会将锁粗化到操作外部。

轻量级锁

先用CAS尝试将 mark word更新为指向Lock record的指针,并把锁标志位变为00,表示轻量级锁定。

如果失败了,检查mark word是否指向当前线程栈帧,是,则说明当前线程已经拥有该锁。

否则,mark word标志位膨胀为10,升级为重量级锁,后面等待锁的进程需要阻塞。

解锁部分也是CAS操作。在有竞争的情况下,轻量级锁比重量级锁多了CAS操作。

 

偏向锁,在无竞争时,把整个同步都消除了,CAS都省略了,会偏向于第一个获得它的线程。

使用CAS把线程ID记录到mark word。

如果有另外线程请求锁时,偏向锁生涯结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值