Synchronized
pulbic class Something(){ public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} }
static加synchronized就是类锁,通常就相当于Something.cSyncA()这样来调用
a. x.isSyncA()与x.isSyncB() 都是对同一个实例(x)的synchronized域访问,因此不能被同时访问。
b. x.isSyncA()与y.isSyncA() 是针对不同实例的,因此可以同时被访问(对象锁对于不同的对象实例没有锁的约束)
c. x.cSyncA()与y.cSyncB() 因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与 Something.isSyncB()了,因此不能被同时访问。
d. x.isSyncA()与Something.cSyncA() 这里是关键,会同时被访问,这里的类锁和对象锁并不冲突
结论:
1. synchronized static是某个类的范围,synchronized static cSync{} 防止多个线程同时访问多个实例的synchronized static 方法。它可以对类的所有对象实例起作用。
2. synchronized 是某实例的范围,synchronized isSync(){} 防止多个线程同时访问一个实例的synchronized 方法。
对于同步代码块和同步方法之间的关系:
synchronized methods(){} 与synchronized(this){}之间没有什么区别,只是synchronized methods(){} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率。
对于synchronized(this){}要记住必须是同一个对象才会有效
这里有时候会synchronized(Object o){}这样写,这里传入的对象也必须是同一个,这样同步的方法才会有用
同样的synchronized(Something.class){}和static synchronized也是没什么区别,你也可以把这段代码块当做是对整个类的锁
对于同步代码块和同步方法如何去实现同步的?
同步代码块:一个同步代码块总是要持有一个java对象的,任何对象都有一个monitor与之相关联。monitor对象中monitorenter指令表示同步代码块的开始位置,monitorexit指令表示同步代码块的结束位置,线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获取对象的锁;
同步方法:在同步方法修饰符上有ACC_SYNCHRONIZED标志,我们通过它来区分是否是一个同步方法。
同步不具有继承性:
如果父类有一个带synchronized关键字的方法,子类继承并重写了这个方法。
但是同步不能继承,所以还是需要在子类方法中添加synchronized关键字。
为什么字符串常量池中的字符串不能作为锁对象?
字符串常量池中的字符串只有一份,所以synchronized(string)在使用时某些情况下会出现一些问题,比如两个线程运行 synchronized(“abc”)}和 synchronized(“abc”){}修饰的方法时,这两个线程就会持有相同的锁,导致某一时刻只有一个线程能运行。所以尽量不要使用synchronized(string)而使用synchronized(object)
Synchronized关键字的底层原理:
同步代码块:JVM 是通过进入、退出对象监视器( Monitor )来实现对方法、同步块的同步的。具体实现是在编译之后在同步方法调用前加入一个 monitor.enter 指令,在退出方法和异常处插入 monitor.exit 的指令。其本质就是对一个对象监视器( Monitor )进行获取,而这个获取过程具有排他性从而达到了同一时刻只能一个线程访问的目的。而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit 之后才能尝试继续获取锁。
同步方法:依靠的是方法修饰符上的 ACC_SYNCHRONIZED 实现。Synchronized 方法则会被翻译为普通的方法调用和返回指令,比如 invokevirtual 指令,在 jvm 字节码层面并没有任何特别的指令来实现synchronized 修饰的方法,而是在 class 文件的方法表中将该方法的 access_flags 字段中的synchronized 标志位置为 1,表示该方法为 synchronized 方法,且使用调用该方法的对象 or该方法所属的 class 在 jvm 内部对象表示作为锁对象。
所以在同步块中发生异常也是会释放锁的。
可重入锁:
可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁。
如果没有可重入锁,在对象内部方法调用另外一个同步方法机会出现死锁,或者释放当前锁,再去获取下一个锁,这样的调度切换是很耗资源的。
在ReenTrantLock内部:AQS中维护了一个private volatile int state来计数重入次数,避免了频繁的持有释放操作,这样既提升了效率,又避免了死锁
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10310450.html

浙公网安备 33010602011771号