一:轻量锁和偏向锁的区别:
(1)争夺轻量锁失败时,自旋尝试抢占锁
(2)轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁,线程不会主动释放偏向锁
二:为什么每个对象都能作为一个锁?
java对象 是天生的Monitor,每一个对象都有成为Monitor的潜质,因为在Java设计中,每一个Java对象自打娘胎里出来就带一个看不见的锁,它叫内部锁或者Monitor锁
三:Monitror与java对象以及线程如何关联?
四:锁升级后,hashcode去哪儿了?
五:什么是锁消除?
/**
/**
* @Author:xiaoqi
* @creat 2023/8/12 11:27
* 锁消除
* 从JIT角度看想相当于无视他,synchronized(o)不存在了
* 这个锁对象并没有被共用扩散到其他线程使用
* 极端的说就是根本没有加锁对象的底层机器码,消除了锁的使用
*/
public class LockClearUpDemo {
static Object object = new Object();
public void m1() {
//锁消除问题,JIT会无视它,synchronized(o)每次new出来的,都不存在了,非正常的
Object o = new Object();
synchronized (o) {
System.out.println("-----------hello LockClearUpDemo" + "\t" + o.hashCode() + "\t" + object.hashCode());
}
}
public static void main(String[] args) {
LockClearUpDemo lockClearUpDemo = new LockClearUpDemo();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
lockClearUpDemo.m1();
}, String.valueOf(i)).start();
}
}
}
/**
* -----------hello LockClearUpDemo 229465744 57319765
* -----------hello LockClearUpDemo 219013680 57319765
* -----------hello LockClearUpDemo 1109337020 57319765
* -----------hello LockClearUpDemo 94808467 57319765
* -----------hello LockClearUpDemo 973369600 57319765
* -----------hello LockClearUpDemo 64667370 57319765
* -----------hello LockClearUpDemo 1201983305 57319765
* -----------hello LockClearUpDemo 573110659 57319765
* -----------hello LockClearUpDemo 1863380256 57319765
* -----------hello LockClearUpDemo 1119787251 57319765
*/
六:什么是锁粗化?
/**
* @Author:xiaoqi
* @creat 2023/8/12 12:27
* 锁粗化
* 假如方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器会把这几个synchronized块合并为一个大块
* 加粗加大范围,一次申请锁使用即可,避免次次的申请和释放锁,提高了性能
*/
public class LockBigDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (objectLock) {
System.out.println("111111111111");
}
synchronized (objectLock) {
System.out.println("222222222222");
}
synchronized (objectLock) {
System.out.println("333333333333");
}
synchronized (objectLock) {
System.out.println("444444444444");
}
//底层JIT的锁粗化优化
synchronized (objectLock) {
System.out.println("111111111111");
System.out.println("222222222222");
System.out.println("333333333333");
System.out.println("444444444444");
}
}, "t1").start();
}
}
七:synchronized是怎么实现的?
- 同步方法的常量池中会有一ACC_SYNCHRONIZED 标志。当某个线程要访问某个方法的时候,会检查是否有 ACC_SYNCHRONIZED),如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。
- 同步代码块使用(monitorenter和 monitorexit 两个指令实现。可以把执行(monitorent
er指令理解为加锁,执行(monitorexit」理解为释放锁。每个对象维护着一个记录着被锁次
数的计数器。未被锁定的对象的该计数器为 0,当一个线程获得锁(执行(monitorenter))
后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行(monitorexit)指令)的时候,计数器再自减。当计数器为 0 的时候。锁将被释放,其他线程便可以获得锁。
八:synchronized锁的是什么?
同步方法:public synchronized void print(){}
public static synchronized void print(){}
同步代码块:synchronized (this){}
synchronized (Xxx.class){}
总结一下:
- synchronized的普通方法,其实锁的是调用这个方法的实例对象,而synchronized的静态方法,其实锁的是这个方法属于的类对象。
- synchronized(this),其实锁的是this这个实例对象,而synchronized(Xxx.Class),其实锁的是这个类对象。
一个类只有一个类对象,但是有很多个实例对象。
九:synchronized是如何保证原子性、可见性、有序性的?
- synchronized如何保证的原子性呢?
synchonized其实是通过 monitorenter 和 monitorexit 这两个字节码指令实现的。当线程执行到 monitorenter 的时候要先获得锁,才能执行后面的方法。当线程执行到monitorexit 的时候则要释放锁,在未释放之前,其他线程是无法再次获得锁的,所以,通过monitorenter和monitorexit指令,可以保证被synchronized修饰的代码在同一时间只能被一个线程访问,在锁末释放之前,无法被其他线程访问到。 - synchronized如何保证有序性?
由于synchronized修饰的代码,同一时间只能被同一线程访问。那么也就是单线程执行的。所以,可以保证其有序性。 - synchronized如何保证可见性?