java面试-synchronized底层实现机制

一、synchronized的三种应用方式

1、修饰实例方法,锁是当前实例对象,进入同步代码前要获得当前实例的锁

/**
 * 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);
    }
}  
/**
 * 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对象,进入同步代码前要获得当前类对象的锁 

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,然后试图进行降级。

posted @ 2019-08-29 20:31  与君共舞  阅读(2020)  评论(0编辑  收藏  举报