volatile与synchronized

volatile实现的算法原理:缓存一致性协议——

每个CPU有自己的缓存,当一个变量是共享变量(其他CPU也有此变量的副本),某个处理器在更新时通知其他CPU将该该变量的缓存行置为无效状态并将更新回写到共享内存,当其他CPU需要使用时由于发现其缓存的变量是无效的,便会重新从内存中读取。

volatile的不安全性:

A正确加载了s,加载后加1操作时B也对s进行了加1操作,但B只是让s加了1还没执行最后的putstatic指令,因此A是不会重新加载s的,这就意味着两次加1只增大了一个数,必然结果有问题。

 

 

synchronized:

synchronized的三种使用形式:

普通同步方法,锁的是当前实例对象。

静态同步方法,锁的是Class对象,所有该类型的对象都会被锁。

同步块:锁的是括号里配置的对象。

每个对象有一个监视器锁,线程执行monitorenter指令时尝试获取monitor的所有权:

1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2.如果线程已经占用该monitor,只是重新进入,则进入monitor的进入数加1。

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,知道monitor的进入数为0,再重新尝试获取monitor的所有权。

执行monitorexit的线程必须是monitor的所有者,指令执行时,monitor的进入数减1,减1后进入数若为0线程退出monitor,退出的同时:将共享资源同步回主内存,并通过主内存通知其他等待的线程来获取monitor。

 

java对象头:

所谓的锁是存在java对象头里的,对象头里的MarkWord存放对象的hashcode,分代年龄和锁信息。运行期间MarkWord里的数据会随锁标志位的变化而变化。

 

JDK1.6为了减少获得锁和释放锁的性能消耗,引入了“偏向锁”和“轻量级锁”。共有四种状态,从低到高依次是无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态。锁可以升级但不能降级。

偏向锁:

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得。由此引入了偏向锁的机制,当一个线程访问同步块时在对象头和栈帧的锁记录里存储锁偏向的线程id,以后该线程访问同步块时不需要进行CAS操作来加锁和解锁,而是查看对象头里是否存有当前线程的偏向锁(对象头里锁偏向的线程id是否是当前线程id),不是的话使用CAS竞争偏向锁(应该是修改对象头偏向锁id的过程)。

偏向锁使用了一种等到竞争出现才释放锁的机制:竞争线程出现时暂停当前线程,检查对象头的偏向锁记录,要么指向新线程,要么标记对象不适合作为竞争线程的偏向锁,或恢复到无锁状态,最后唤醒暂停的线程。

偏向锁默认开启,可使用-XX:-UseBiasedLocking=false来关闭,关闭后程序默认进入轻量级锁状态。

轻量级锁:

轻量级锁加锁--线程执行同步块前,在栈帧中创建锁记录空间,并将MarkWord复制到该空间,然后线程使用CAS操作把MarkWord替换为指向锁记录的指针。成功则获得锁,失败表示其他线程竞争锁,当前线程尝试使用自旋锁来获取锁。

轻量级锁解锁:解锁时使用CAS替换MarkWord中的指针为原来对象头中内容,替换成功解锁成功,替换失败表示当前锁存在竞争,直接升级为重量级锁。

重量级锁:重量级锁被其他线程获取时,会导致其他线程阻塞。(重量级锁的竞争方式不使用自旋锁竞争方式——参与竞争线程直接阻塞,轻量级锁的竞争方式使用自旋锁竞争方式)

自旋锁不是锁,自旋锁指的是获取锁的方式。

 

posted @ 2017-09-06 16:27  新生的小心情  阅读(217)  评论(0编辑  收藏  举报