对象在堆中的结构
对象头包括两个部分,第一部分用于存储自身运行时的数据例如GC标志位、哈希码、锁状态等信息。第二部分存放指向方法区类静态数据的指针。
实例变量存放类的属性数据信息,包括父类的属性信息。如果是数组的实例部分还包括数组的长度。这部分内存按4字节对齐。
填充数据区域的存在是因为虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。HotSpot VM的自动内存管理要求对象起始地址必须是8字节的整数倍。对象头本身是8的倍数,当对象的实例变量数据不是8的倍数,便需要填充数据来保证8字节的对齐。另外,堆上对象内存的分配是并发进行的.
对象头中的大致信息如下
其中tag的两个字节用来显示锁的类型。通常我们说synchronized的对象锁,就是这里Tag=10时的monitor对象,这里的Monitor address就是这个monitor对象(就是重量锁)的地址。
新请求的线程会先被加到一个线程排队队列中,当某个持有锁的线程unlock后,则排队的线程竞争锁。当执行线程的wait()方法后,线程释放锁,并进入wait线程集合,当调用notify()或notifyAll()后,线程重新进入排队队列。
synchronized中的锁一般分为重量锁(对象锁),自旋锁,自适应自旋锁,轻量锁,偏向锁
使用synchronized关键字的代码块在加锁时,不会直接就加重量锁,因为重量锁对系统的开销最大。若一个线程等待获取锁对象所持续的时间非常短,这时适合使用自旋锁。所谓自旋锁,就是等待锁的线程并不进入阻塞状态,而是执行一个无意义的循环。在循环结束后查看锁是否已经被释放,若已经释放则直接进入执行状态。因为长时间无意义循环也会大量浪费系统资源,因此自旋锁适用于间隔时间短的加锁场景。
自适应自旋锁:系统根据运行时的统计信息,来调整自旋的次数。
轻量锁和偏向锁适用于没有线程竞争的情况。无法代替重量锁
参考资料点击这里