【Java多线程学习6】synchronized关键字

【Java多线程学习6】synchronized关键字

一、synchronized关键字是什么?有什么作用?

synchronized关键字是Java线程同步的关键字其可以修饰方法或代码块,并可以保证其修饰的方法代码块在任意时刻只能有一个线程执行

synchronized关键字使用主要有下面3种:

  • 修饰代码块
  • 修饰实例方法
  • 修饰静态方法

其中
synchronized关键字加到static静态方法和synchronized(class)代码块上都是给class类上锁。
synchronized关键字加到实例方法或synchronized(object)上是给对象实例上锁。

1、synchronized修饰代码块(锁指定对象/类)

    private static final Object object1 = new Object();
     public static void deal1() {
        //同步代码,获取对象锁
         synchronized (object1) {
            //需要同步的操作
        }
        
        //同步代码,获取class类锁
        synchronized (practice3.class) {
             //需要同步的操作
        }
    }

2、synchronized修饰实例方法(锁当前对象实例)

给当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

    public synchronized void dealIt() {
        //需要同步的操作
    }

3、synchronized修饰静态方法(锁当前类)

当前类加锁,进入同步代码块前要获得当前class的锁(会作用于类的所有对象实例)。
因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,所以被类的所有实例共享。

    public synchronized static void dealIt() {
        //需要同步的操作
    }

问题思考1:

被synchronized修饰的静态方法和非静态方法之间调用互斥吗?
答:
不互斥,如果线程A调用的是实例对象所属的类的静态synchronized方法,线程B调用的是实例对象的非静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是当前类的锁,而非静态synchronized方法所占用的锁是当前实例对象的锁

问题思考2:

构造方法可以使用synchronized修饰吗?
答:
构造方法不能使用synchronized关键字修饰
因为构造方法本身就属于线程安全的,不存在同步构造方法一说。

二、synchronized底层原理

1、对于synchronized同步代码块情况

public class synchronized1{
	public void deal() {
		synchronized(synchronized1.class) {
			System.out.println("Hello wys");
		}
	}
}

执行反编译,并查看反编译后的内容
在这里插入图片描述

在这里插入图片描述

说明:先将.java文件编辑为.class字节码文件,然后将.class文件反编译查看其反编译后的内容,反编译后的内容如下:
在这里插入图片描述
从上面反编译后的内容中我们可以看到:synchronized同步语句块是通过使用 monitorentermonitorexit两个指令。其中monitorenter指令指向同步代码块的开始位置monitorexit指令则指向同步代码块的结束位置

在执行monitorenter时,当前线程会尝试获取锁对象的monitor的持有权(对象监视器monitor),当monitor里的计数器为0就可以获取,并将计数器加1。如果当前线程获取锁失败,那当前线程就要阻塞等待,直到锁被另一个线程释放为止。

在执行monitorexit指令后,将锁计数器设为0,表示锁被释放,其他线程可以尝试获取锁。(注意:当前线程拥有锁对象才能执行monitorexit指令)

2、synchronized修饰方法的情况

public class synchronized1{
	public synchronized void deal1() {
			System.out.println("Hello wys");
	}
}

在这里插入图片描述
我们从反编译的结果可以看到,synchronized修饰方法并没有monitorenter指令和monitorexit指令,取而代之使用的是ACC_SYNCHRONIZED标识。该标识指明此方法为一个同步方法。JVM通过该标识ACC_SYNCHRONIZED标识来辨别一个方法是否为同步方法,从而进行相应的同步调用。

如果是实例方法,JVM会尝试获取实例对象的锁,如果是静态方法JVM会尝试获取当前class的锁。

总结:

  • 1、synchronized同步代码块底层实现是通过monitorentermonitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。
  • 2、synchronized修饰方法并没有monitorenter和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标识,该标识指明了该方法是一个同步方法。
  • 不过两者的本质都是对对象监视器monitor的获取。

三、synchronized和volatile有什么区别?

synchronized关键字 和 volatile关键字 两者是互补存在的而不是对立存在的

  • volatile关键字是线程同步的轻量级实现。volatile关键字可以保证变量的可见性,但是不能保证对变量操作的原子性。而synchronized关键字两者都能保证
  • volatile关键字只能修饰变量,而synchronized关键字可以修饰方法和代码块
  • volatile主要是用于解决多线程环境下变量的可见性,而synchronized主要是用于解决多线程环境下访问资源的同步性

四、JDK1.6之后synchronized底层做了哪些优化?

JDK1.6对锁的实现引入了大量的优化,如如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。

加入偏向锁和轻量锁后,锁就有四种状态级别由低到高:无锁-》偏向锁-》轻量级锁-》重量级锁。

具体详细优化内容后续文章介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值