同步关键字是Java并发的重要组成部分,它可以解决多个线程之间资源的同步。
由于同步是一个关键字,因此可以在三个位置修改代码:实例方法,静态方法和代码块。
当同步修改一个实例的方法时,其锁定对象是当前对象实例:
因为锁定对象是当前对象实例,因此如果对象实例不同,则无法保证线程。
同步修改静态方法时,其锁定对象是类的类:
因为锁定对象是当前类的类对象,即使对象实例有所不同,只要范围在此类中,则可以同步线程。
代码块相对特别,它需要指定锁定对象是谁:
我不知道您是否很好奇,如何同步确保线程同步?我们以程序为例:
以下指令集通过反编译获得:
不难发现我们的业务代码包裹在两个说明中,即和谐。事实证明,同步的底层由C ++编写的监视器实现。有关特定详细信息,您可以检查基础源代码objectMonitor.cpp:
从说明的集中度,我们还可以找到一些细节。第14条和20条说明是Monitorexit。根据我们的理解,Monitorenter是一个锁,Monitorexit已解锁。该程序是否解锁了两次?实际上,我们发现该程序的例外表:
从这个结论中,当第5至15条的指令异常时,JVM将直接进入第18条指令继续执行,也就是说,当我们锁定的业务异常时,JVM将自动自动出现。锁。
在JVM中,内存中对象的存储结构可以分为以下三个区域:
在对象头中,有两个部分,它们是类型的指针和数据(也称为Mark Word),用于指定当前对象的类型。这只是一个简单的示例):
在JDK1.6之后,正式升级的同步化(制造同步)具有四种类型的锁,即没有锁,有偏见的锁,轻量级锁和重量级锁定。一个区域用于存储锁定徽标位置以区分同步的锁定状态,如图所示:图:
在64 -bit JVM中,对象头总计12个字节,其中类型指针占4个字节32-bit,Mark Word part占8字节的64位。
从上表可以看出,与锁定量相对应的锁状态,例如01表示没有锁定或偏置锁,如果锁定锁定,则需要在时间戳上记录线程ID和偏差;锁。
将锁分为四个州有什么好处?事实证明,在JDK1.6之前,同步始终将重量级锁的形式用于该程序,这导致了其低性能。jdk1.6后,同步并不增加重量级直接锁。
例如,当线程访问同步代码时,它将在对象头的标记单词中记录线程ID。将来,当线程输入并退出同步代码时,您只需要比较标记字中的线程ID是否匹配它。它意味着获得锁定(可以看到同步化是复发锁),否则它将使用CAS将标记字中的线程ID设置为当前线程。当某个代码块总是只有一个线程输入和退出时,设置偏置锁定可以极大地提高性能,因为锁定的过程而无需锁定锁定的过程只是在Mark Word中判断数据值。
偏向锁的是释放锁直到比赛出现的机制。当某个线程想要与锁竞争时,持有锁的线程需要释放,但是必须释放全球安全点的外观。这可以释放。这可以释放。这可以释放。可以释放这一点。当您需要检查持有锁的线程是否生存时,如果您生存,则需要将标记字中的线程ID重新指定为线程或设置为锁定状态;否则,它将直接设置为无锁定状态。
当偏置锁定由两个线程竞争时,锁定无效,锁升至轻质锁。
如果您自己的锁定记录指针成功,您将获得锁定。目前,其他线程的CAS操作将失败,其他线程输入旋转。当业务执行完成后,线程A继续使用CAS操作将Mark Word在上一个C到C中重新插入。成功,解锁成功。如果失败,锁将升级到重量级锁。
应当指出的是,当线程在置于锁定时等待锁定时,为了确保效率,其旋转受到限制。默认的旋转是10次。锁定也将升级到重量级锁。
当锁定升级到重量级锁时,同步在JDK1.6之前返回该州,并且底层仍然取决于C ++实现的监视器。
通过上述内容,我们可以将与锁同步进行比较: