请带着如下问题阅读本文。
1.什么是自旋锁?
2.什么是乐观和悲观锁?
3.锁的等级:方法锁、对象锁、类锁?
4.如何检测死锁?怎么预防死锁?
5.同步方法和同步块,哪个是更好的选择?
6.Java Concurrency API中的Lock接口(Lock interface)是什么?对比同步它有什么优势?synchronized和ReentrantLock的区别?
7.volatile关键字的作用?
8.锁的优化方法(5种)
9.什么是CAS,优点缺点
10.单例模式的线程安全性?
JAVA中的锁
synchronized(包含偏向锁,轻量级锁,重量级锁,自旋锁)
实现Lock接口的有(读写锁,可重入锁)
这2个的优缺点?
synchronized,是系统提供的。开始性能不及可重入锁,JDK1.7后做了优化,已经一样快了。
好处就是会自动释放锁,代码实现方便。
可重入锁的缺点,就是都需要自己写FINALLY 去释放。但是使用起来更加灵活。
他可以使锁变公平。
在线程等待锁的时候响应中断
可以让线程尝试获取锁。
可以设置超时。
上述2种都是独占锁,就是你占用了以后,别人必须等你释放才能继续用。独占锁是悲观锁。
乐观锁是一种思想,乐观锁假设一般情况数据不会冲突,所以只有在提交更新时,才会对数据冲突进行检测。一般用CAS实现。
CAS是利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法,实现原子操作。是直接调用CPU 的cmpxchg(是汇编指令)指令。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
优点:确保对内存的读-改-写操作都是原子操作执行
缺点: 冲突严重,大量消耗CPU资源。
SYNCHRONIZED 使用
锁方法=锁改类实例
锁对象=锁对象实例
锁静态方法=锁对象的CLASS类
那么锁方法 好 还是 在方法里 锁对象好呢?
这就是同步方法和同步块哪个好?
一般来说同步块是更好的选择,因为它不会锁住整个对象(当然也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
只有当这个方法不需要同步的运行的极快时,我们可以锁方法来提速。因为加锁释放锁也是要开销的。这个就是锁粗化的优化策略。
死锁
死锁要发生,首先要拿独占锁。
拿到独占锁后,这个锁不可被剥夺,如没有超时释放机制。
随后拿着这个锁的时候,还要去拿别的锁,不然我这个锁也不放。
上述3个条件满足后。
加上第四个条件就会死锁。
第四,有多个这样的线程,形成头尾相接的循环等待状态。
如何检测死锁,jvm提供的2个工具都可以查看死锁。
一个是JCONSOLE, 一个是JSTACK
如何预防死锁
按照顺序加锁,给每个OBJECT算一个HASH值,然后更具HASH值大小排序,然后按照这个大小去加锁。
还有就是设置超时。
锁优化方法
减少锁的持有时间(同步方法改同步块)
减少锁的粒度(CONCURRENT HASHMAP)
锁分离(读写锁,读锁写锁)(LinkedBlockingQueue,PUT一把锁,锁尾巴,TAKE一把锁锁头)
锁粗化
在即时编译的时候,如果发现不可能被共享的对象,则可以消除这些锁。分析方法可以参见我写的5篇文章搞定JVM里的第一篇。
自旋锁
当竞争存在时,如果线程可以很快获得锁,可以不再OS层挂起,让线程做几个空转。 JDK1.7,去掉此参数改为内置实现。
volatile作用
概括的说,保证内存可见性,防止指令重排序。
深里说,通过插入内存屏障,来强制CPU的缓存失效来保证内存可见性。通过插入内存屏障阻止编译器,操作系统,内存做指令重排。
想了解细节,看指令重排序
从实践角度说,VOLATILE 加CAS 构成了AQS,而AQS是所有JAVA CONCURRENT UTIL的基石。
单例模式的线程安全性
饿汉
public class MySingleton {
private static MySingleton instance = new MySingleton();
private MySingleton(){}
public static MySingleton getInstance() {
return instance;
}
}
静态内置类
public class MySingleton {
//内部类
private static class MySingletonHandler{
private static MySingleton instance = new MySingleton();
}
private MySingleton(){}
public static MySingleton getInstance() {
return MySingletonHandler.instance;
}
}