java面试-synchronized底层实现机制
一、synchronized的三种应用方式
1、修饰实例方法,锁是当前实例对象,进入同步代码前要获得当前实例的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /** * synchronized修饰实例方法,当前线程的锁是实例对象accountingSync * 当一个线程正在访问一个对象的synchronized实例方法,那么其他线程不能访问该对象的其他synchronized方法 * 一个对象只有一把锁 */ public class AccountingSync implements Runnable { static AccountingSync accountingSync = new AccountingSync(); //共享资源 static int i = 0 ; static int j = 0 ; public synchronized void increase() { i++; } @Override public void run() { for ( int i = 0 ;i< 1000000 ;i++){ synchronized ( this ){ increase(); } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(accountingSync); Thread thread2 = new Thread(accountingSync); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(i); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /** * thread1访问实例对象obj1的synchronized方法,thread2访问实例对象obj1的synchronized方法 * 这样是允许的,因为两个实例对象锁并不相同。 * 此时如果两个线程操作数据非共享,线程安全有保证,如果数据共享,线程安全无法保证 * */ public class AccountingSyncBad implements Runnable { static int i = 0 ; public synchronized void increase() { i++; } @Override public void run() { for ( int i = 0 ; i < 1000000 ; i++) { increase(); } } public static void main(String[] args) throws InterruptedException{ //new新实例 Thread thread1 = new Thread( new AccountingSyncBad()); //new新实例 Thread thread2 = new Thread( new AccountingSyncBad()); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(i); } } |
2、修饰静态方法,锁是当前类的 class对象,进入同步代码前要获得当前类对象的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class AccountingSyncClass implements Runnable{ static int i = 0 ; /** * synchronized作用于静态方法,锁是当前class对象 */ public static synchronized void increase() { i++; } /** * increase4Obj方法是实例方法,其对象锁是当前实例对象, * 如果别的线程调用该方法,将不会产生互斥现象,毕竟锁对象不同, * 但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。 */ public synchronized void increase4Obj(){ i++; } @Override public void run() { for ( int i = 0 ; i < 1000000 ; i++) { increase(); // increase4Obj(); } } public static void main(String[] args) throws InterruptedException{ //new新实例 Thread thread1 = new Thread( new AccountingSyncClass()); //new新实例 Thread thread2 = new Thread( new AccountingSyncClass()); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(i); } } |
3、修饰代码块
synchronized(this) 锁是当前实例对象,
synchronized(AccountingSync.class) 锁是class对象
二、synchronized代码块底层原理
synchronized代码块是由一对monitorenter和monitorexit指令实现的,Monitor对象是同步的基本实现单元。
现代java虚拟机对sychronized进行了优化,引入了偏斜锁、轻量级锁、重量级锁
三、java虚拟机对Synchronized的优化
JVM优化synchronized运行的机制,当JVM检测到不同的竞争情况时,会自动切换到适合的锁实现
1、当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS操作,在对象头上的Mark Word部分设置线程ID,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。
2、有竞争出现时,当有另外的线程试图锁定某个已经被偏斜锁锁定的对象,jvm就会撤销revoke偏斜锁,并切换到轻量级锁。轻量级锁依赖CAS操作Mark Word来试图获取锁,如果成功,就使用轻量级锁,否则继续升级未重量级锁
PS:锁降级也是存在的,当JVM进入SafePoint安全点的时候,会检查是否有闲置的Monitor,然后试图进行降级。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步