synchronized是Java编程语言中的一个关键字,用于实现线程同步。在多线程编程中,多个线程可能同时访问共享资源,而这可能导致数据不一致或其他问题。为了避免这些问题,可以使用 synchronized 关键字来确保在任何时候只有一个线程可以访问共享资源。
使用synchronized就是为了保证线程的三个特性,及原子性、可见性、有序性。原子性:一个或多个操作时,保证要么全部执行成功,要么都不执行。可见性:一个线程操作时,另外的线程可以看见,里面的数据变化情况能看见。有序性:线程安装顺序进行操作。使用锁就是为了保证线程的安全。
public synchronized void lock(){ //方法 } ? public void lock() { ? ?// 一些非关键代码 ? ? ?synchronized (lockObject) { ? ? ? ?// 需要同步的代码块 ? } ? ? ?// 一些非关键代码 }
synchronized可以修饰静态方法、成员函数,同时还可以直接定义代码块。
在Java虚拟机(JVM)中,对象的内存结构主要分为三部分:对象头、实例数据、对齐填充。
对象头:
存储对象自身的运行时数据,比如哈希码、垃圾回收信息、同步锁等。
不同的虚拟机实现会在对象头中存储不同的信息,例如,在HotSpot虚拟机中,对象头占用的空间大小是与平台相关的。
实例数据:
存储对象的实际数据,即类的字段内容。
对于数组类型的对象,还包括数组长度等信息。
实例数据的大小取决于对象所属的类的字段定义。
对齐填充:
由于虚拟机的内存分配规则,对象的大小通常要求是8字节的整数倍。
对齐填充用于填充对象的实例数据,以满足对齐要求,使得对象的起始地址是8字节的整数倍。
对齐填充不包含实际的数据,仅仅是为了对齐。
对象头中Mark Word是重点
监视器(Monitor)图:借图
这个是获取Monitor和释放Monitor的流程。
在jdk1.6之前synchronized被称为重量级锁,在jdk1.6之后,进行了优化,引入了偏向锁和轻量锁。
原理:
当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为“01”,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的MarkWord之中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高。
好处:
偏向锁是在只有一个线程执行同步块时进一步提高性能,适用于一个线程反复获得同一锁的情况。偏向锁可以提高带有同步但无竞争的程序性能。
原理:
将对象的Mark Word复制到栈帧中的Lock Recod中。Mark Word更新为指向Lock Record的指针。
好处:
在多线程交替执行同步块的情况下,可以避免重量级锁引起的性能消耗。
轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。 ? 自旋锁:许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得,通过让线程执行循环等待锁的释放,不让出CPU。如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式。但是它也存在缺点:如果锁被其他线程长时间占用,一直不释放CPU,会带来许多的性能开销。 ? 自适应自旋锁:这种相当于是对上面自旋锁优化方式的进一步优化,它的自旋的次数不再固定,其自旋的次数由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定,这就解决了自旋锁带来的缺点。
参考:
https://www.cnblogs.com/three-fighter/p/14396208.html