synchronized中文意思是同步,也称之为同步锁。
synchronized有哪些用途是保证在同一时刻, 被修饰的代码块或办法只能有一个线程实行,以达到保证并发安全的成效。
synchronized是Java中解决并发问题的一种最常见的办法,也是最简单的一种办法。
在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会看上去那样笨重,伴随Javs SE 1.6对synchronized进行的各种优化后,synchronized并不会看上去那样重了。
synchronized有哪些用途主要有三个:
原子性: 确保线程互斥地访问同步代码;
可见性: 保证共享变量的修改可以准时可见,其实是通过Java内存模型中的 对一个变量unlock操作之前,需要要同步到主内存中;假如对一个变量进行lock操作,则将会清空工作内存中此变量的值,在实行引擎用此变量前,需要重新从主内存中load操作或assign操作初始化变量值 来保证的;
有序性: 有效解决重排序问题,即 一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作;
synchronized的3种用方法:
修饰实例办法: 用途于目前实例加锁
修饰静态办法: 用途于目前类对象加锁
修饰代码块: 指定加锁对象,对给定对象加锁
1.修饰办法Synchronized修饰一个办法非常简单,就是在办法的前面加synchronized,synchronized修饰办法和修饰一个代码块类似,只不过用途范围不同,修饰代码块是大括号括起来的范围,而修饰办法范围是整个函数。
办法1、修饰的是一个办法
publicsynchronizedvoidmethod(){//todo}
办法2、修饰的是一个代码块
publicvoidmethod(){synchronized(this){//todo}}
办法一与办法二是等价的,都是锁定了整个办法时的内容。
synchronized关键词不可以继承。虽然能用synchronized来概念办法,但synchronized并不是办法概念的一部分,因此,synchronized关键词不可以被继承。
在子类办法中加上synchronized关键词
classParent{publicsynchronizedvoidmethod(){}}classChildextendsParent{publicsynchronizedvoidmethod(){}}
在子类办法中调用父类的同步办法
classParent{publicsynchronizedvoidmethod(){}}classChildextendsParent{publicvoidmethod(){super.method();}}
注意:
在概念接口办法时不可以用synchronized关键词。
架构办法不可以用synchronized关键词,但可以用synchronized代码块来进行同步。
当有一个明确的对象作为锁时,就能用类似下面如此的方法写程序:
publicvoidmethod3(SomeObjectobj){//obj锁定的对象synchronized(obj){//todo}}
当没明确的对象作为锁,只不过想让一段代码同步时,可以创建一个特殊的对象来充当锁:
classTestimplementsRunnable{privatebyte[]lock=newbyte[0];//特殊的instance变量publicvoidmethod(){synchronized(lock){//todo同步代码块}}publicvoidrun(){}}
2.修饰一个静态办法
synchronized也可修饰一个静态办法,使用方法如下:
publicsynchronizedstaticvoidmethod(){//todo}
3.修饰一个类
Synchronized还可用途于一个类,使用方法如下:
classClassName{publicvoidmethod(){synchronized(ClassName.class){//todo}}}
用总结
无论synchronized关键词加在办法上还是对象上,假如它用途的对象是非静态的,则它获得的锁是对象;假如synchronized用途的对象是一个静态办法或一个类,则它获得的锁是对类,该类所有些对象同一把锁。
每一个对象只有一个锁(lock)与之有关联,哪个拿到这个锁哪个就能运行它所控制的那段代码。
达成同步是要非常大的系统开销作为代价的,甚至可能导致死锁,所以尽可能防止无谓的同步控制。
3、synchronized的底层达成谈synchronized的底层达成,就不能不谈数据在JVM内存的存储:Java对象头,与Monitor对象监视器。
在JVM中,对象在内存中的布局分为三块地区:对象头、实例数据和对齐填充。如下图所示:

实例数据: 存放类的属性数据信息,包含父类的属性信息;
对齐填充: 因为虚拟机需要 对象起始地址需要是8字节的整数倍。填充数据不是需要存在的,只是为了字节对齐;
对象头: Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit),但假如对象是数组种类,则需要3个机器码,由于JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但没办法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。
synchronized用的锁就是存在Java对象头里的,那样什么是Java对象头呢?Hotspot虚拟机的对象头主要包含两部分数据:Mark Word(标记字段)、Class Pointer(种类指针)。
其中 Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是什么类的实例,Mark Word用于存储对象自己的运行时数据,它是达成轻量级锁和偏向锁的重点。
任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状况。
synchronized在JVM里的达成都是 基于进入和退出Monitor对象来达成办法同步和代码块同步,虽然具体达成细节不同,但都可以通过成对的MonitorEnter和MonitorExit指令来达成。
MonitorEnter指令: 插入在同步代码块的开始地方,当代码实行到该指令时,将会尝试获得该对象Monitor的所有权,即尝试获得该对象的锁;
MonitorExit指令: 插入在办法结束处和异常处,JVM保证每一个MonitorEnter需要有对应的MonitorExit;
那什么是Monitor?可以把它理解为 一个同步工具,也可以描述为 一种同步机制,它一般被描述为一个对象。
与所有皆对象一样,所有些Java对象是天生的Monitor,每个Java对象都有成为Monitor的潜质,由于在Java的设计中 ,每个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁。
也就是一般说Synchronized的对象锁,MarkWord锁标识位为10,其中指针指向的是Monitor对象的起始地址。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor达成的。
锁解决了数据的安全性,但同样带来了性能的降低。hotspot 虚拟机的作者经过调查发现,大多数状况下,加锁的代码不只没有多线程角逐,而且一直由同一个线程多次获得。所以基于如此一个概率。
synchronized 在JDK1.6 之后做了一些优化,为了降低获得锁和释放锁来的性能开销,引入了偏向锁、轻量级锁、自旋锁、重量级锁,锁的状况依据角逐激烈的程度从低到高不断升级。
锁主要存在四种状况,依次是:无锁状况、偏向锁状况、轻量级锁状况、重量级锁状况,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁。但锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。而且这个过程就是开销渐渐加强的过程。






