ReentrantLock的原理是改变抽象队列同步器中的state值来判断是否加锁
synchronized的原理是 修改对象头中的值
syn关键字
在JDK1.6之前是重量级锁
原因:
调用了操作系统的函数,
这里涉及到CPU 内核态与用户态的转换
JDK1.6之后做了优化
尽量在JVM层面解决锁的问题 但还是会调用操作系统函数
Synchronized修饰方法与代码块的异同
我们从编译过的文件可以看出直观异同
java代码:
public synchronized void test2() {
System.out.println("test2");
}
private String flag = "flag";
public void test4() {
synchronized (flag) {
System.out.println("test4");
}
}
编译后文件:
结论:
同步方法,JVM使用ACC_SYNCHRONIZED标识来实现。即JVM通过在方法访问标识符(flags)中加入ACC_SYNCHRONIZED来实现同步功能。
同步代码块,JVM使用monitorenter和monitorexit两个指令实现同步。即JVM为代码块的前后真正生成了两个字节码指令来实现同步功能的
Synchronized修饰静态方法和非静态方法区别
修饰静态方法时,是对类对象(.class)进行加锁
修饰非静态方法时,是对当前实例(this)进行加锁
Synchronized和lock的区别?
1、Synchronized是JVM层面,monitorenter和monitorexit,底层是通过monitor对象来完成加锁
wait,notify也依赖于monitor对象
lock是api层面
2、synchronized不需要手动释放锁,lock需要手动释放
3、等待是否可中断
synchronized不可中断 除非执行完成或发生异常
lock 可以中断,trylock加锁释放时间或者调用interrupt()方法
4、synchronized -非公平锁
lock 两者均可
5、condition概念
synchronized没有
lock 用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,synchronized只能随机或者全部notifyAll
死锁
参考链接:https://www.cnblogs.com/bopo/p/9228834.html
发生死锁4个必要条件
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
如何避免死锁
让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。
将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。
将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。
Synchronized如何保证不死锁
monitorenter之后有两处退出 monitorexit,其中一个是正常退出时调用 monitorexit,另一个是异常发生时 调用。