Java关键字 可以用来修饰通常用来修饰方法,代码块。能够确保在同一时刻最多只有一个线程运行该代码,防止并发操作!

 

  1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块之后才能得以执行该代码块。
  2. 不过,当一个线程访问object的一个synchronized(this){}同步代码块时,其他线程对object中所有非synchronized(this)同步代码块允许访问。
  3. 重点,当一个线程访问object的synchronized(this){}同步代码块时,其他线程对object中其他synchronized(this){}同步代码块的访问也会被阻塞。
  4. 以上三个例子,对同步代码块,对象锁,同样适用。放一个线程访问object的一个synchronized(this)同步代码时,它就获得这个object的对象锁,结果,其他线程对该object对象所有的同步操作都会被阻塞。

 

同步关键是看是否使用的是不是同一把锁。

比如:

synchronized修饰普通方法,同步锁即为这个方法被调用的对象。如果是同一个对象执行两个同步方法,就可以是实现同步。如果我们创建了两个对象,那个这两个同步方法也不能实现同步。

synchronized修饰静态方法,同步锁即为这个方法被调用的类。如果使用对象调用,因为不是同一把锁,所以并不能实现同步,必须使用Class.fun()才可以实现同步。

 

synchronized同步缺陷

     因为synchronized同步代码锁定了对象锁,阻塞了其他线程对该代码块的调用。如果此代码块比较大,那将大大影响效率。典型的,若将线程类的run()方法声明为synchronized,由于在线程的整个声明周期内他一直在运行,因此将导致对本类任何synchronized的方法永远都得不到调用。

 

解决办法:使用synchronized代码块,语法如下:

     synchronized{

         //允许访问控制的代码 

     }

synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (可以是类实例或类)的锁方能执行,具体机

制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

 

常见问题总结:

说一下synchronized的作用?

对与单一JVM来说,synchronized可以保证在并发情况下,同一时刻只有一个线程执行某个别方法和某段代码,可以用来修饰方法和代码块,实现对同步代码的并发安全控制。

synchronized可用于修饰方法和代码块,他们有什么区别呢?

修饰方法在底层实现上会在方法访问标识中设置ACC_SYNCHORNIZED标识符;

修饰代码块在底层实现上会使用monitorenter和monitorexit指令;

那你说一下修饰方法方式的底层实现原理?
 

那你再说一下修饰代码块方式的底层实现原理?

详细介绍一下monitor?

Java虚拟机中,synchronized支持的同步方法和同步语句都是使用monitor来实现的。每个对象都与一个monitor相关联,当一个线程执行到一个monitor监视下的代码块中的第一个指令时,该线程必须在引用的对象上获得一个锁,这个锁就是monitor实现的。在HotSpot虚拟机中,monitor是由ObjectMonitor·实现,使用C++编写实现,具体代码在HotSpot虚拟机源码ObjectMonitor.hpp文件中。

查看源码我们会发现,主要的属性有_count(记录该线程获取锁的次数),_recursions(所得重入次数),_owner(指向持有ObjectMonitor对象的线程)、_WaitSet(处于wait状态的线程集合)、_EntryList(处于等待锁block状态的线程队列)。

当并发线程执行synchronized修饰的方法和代码块时,先进入_EntryList中,当某个线程获取到对象的monitor后,把monitor对象的_owner变量设置为当前线程,同时monitor对象中的计数器_count加1,当前线程获取同步锁成功。

当synchronized修饰的方法和代码块的线程调用wait()方法时,当前线程将释放持有的monitor对象,monitor对象中的_owner变量赋值为null,同时,monitor对象中的_count值减1,然后当前线程进入_WaitSet集合中等待被唤醒。

 

对象的锁状态存在哪里?

Java对象的对象头中。

对象头中包含哪些内容?

一部分是对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称它为“Mark Word”;

一部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

针对对象头中的锁状态表示来说,synchronized属于哪一级别的锁?

重量级锁