您的位置 首页 java

java中synchronized关键字基础-1

1、 synchronized 关键字简介

synchronized是 Java 中的一个关键字,在中文中为同步,也被称之为’同步锁’,以此来达到 多线程 并发访问时候的并发安全问题,可以用来修饰代码块、非 静态方法 。静态方法等;

修饰代码块时:给当前指定的对象加锁

修饰非静态方法时:作用于当前实例加锁

修饰静态方法时:作用于当前类对象加锁

synchronized在java内存模型中的主要作用

原子性:通过monitorenter和monitorexit指令,保证被synchronized修饰的代码在同一时间只能被一个 线程 访问,在锁未释放之前,无法被其他线程访问到

可见性:保证共享变量的修改能够及时可见,对一个变量的unlock操作之前,必须把此变量同步回主内存中(store和write操作)

有序性:一个变量在同一时刻只允许一条线程对其执行 lock 操作,这条规则决定了持有同一个锁的两个同步块只能串行执行

2、synchronized修饰代码块

当synchronized修饰代码块时,有以下几种情况

1、this关键字

点击查看代码

 synchronized(this){   
//互斥代码
 }  

这里的this就是等价于调用这个方法的对象,synchronized锁的就是this这个对象的锁,若有多个对象调用方法,各个对象锁之间相互独立,互不影响

2、Class.class

点击查看代码

 synchronized(Test.class){     
//互斥代码
}  

这里synchronized锁的对象为类锁,在需要类锁的代码不能同时执行,但是与非需要类锁的对象锁或者与没有加锁的代码可以同时执行

用synchronized进行加锁时看获得锁是对象还是类的锁,还有的是synchronized锁住的是一个对象或者类(其实也是对象),而不是方法或者代码段。

3、synchronized修饰实例方法

点击查看代码

 public  synchronized  void  method(){     //代码
 }  

当synchronized修饰实例方法时,说的是该类的实例对象

4、synchronized修饰静态方法

点击查看代码

 public synchronized  static  void method() {     // todo
 }  

由于static静态方法是属于类的而不属于对象的,所以synchronized修饰的静态方法锁定的是这个类的所有对象

5、synchronized的底层实现原理

在java内存模型中,synchronized可以保证原子性、有序性、可见性,在这之前,先谈谈对象在HotSpot虚拟机中的分布,主要有三部:对象头( Header )、实例数据(Instance Data)和对象填充(Padding)

对象头

对象头主要包括两部分信息,第一部分用于存储对象自身的运行时数据、如哈希码( HashCode )、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称之为’Mark Word’,还有一部分称之为类型指针,即对象指向它的类元数据的指针, 虚拟机 通过这个指针来确定这个对象是哪个类的实例

存储内容标志位状态对象哈希码、对象分代年龄01未锁定指向锁记录的指针00轻量级锁定指向重量级锁的指针10膨胀(重量级锁定)空,不需要记录信息11GC标识偏向线程ID、偏向时间戳、对象分代年龄01可偏向

实例数据

实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。这部分的存储顺序会受到虚拟机分配策略参数(-XX: FieldsAllocationStyle) 和字段在Java源码中定义顺序的影响。

对齐填充

对齐填充并不是必然存在的,也没有特别的含义,仅仅只是起着 占位符 的作用,由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,就是对象的大小必须是8字节的整数倍,而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

原子性

synchronized实现原子性底层是通过 JVM 来实现的,同一时间只能有一个线程去执行synchronized中的代码块;

每一个对象都有一个监视器monitor来关联,监视器被占用时会被锁住,其他线程无法获取该monitor,当JVM执行某个线程的的内部方法的monitorenter,它会尝试去获取该对象的monitor的所有权,过程如下

1、若monitor的进入数为0,线程可以进入monitor,并将该monitor的进入数置为1,那么该线程就成为monitor的所有者

2、若线程已拥有monitor的所有权,允许它重入monitor,则进入monitor的进入数加1(recursions:记录线程拥有锁的次数)

3、若其他线程已经占有monitor的所有权,那么当前尝试获取monitor的所有权的线程会被阻塞,直到monitor的进入数变为0,才能重新尝试获取monitor的所有权。

monitorexit指令

1、能执行monitorexit指令的线程一定是拥有当前对象的monitor的所有权的线程。

2、当执行monitorexit时会将monitor的进入数减1。当monitor的进入数减为0时,当前线程退出monitor,不再拥有monitor的所有权,此时这个monitor阻塞的线程可以尝试去获取这个monitor的所有权。

可见性

synchronized通过内存屏障保证可见性,同样的我们知道 volatile 是通过内存屏障来保证可见性的,

1、monitorenter指令之后,synchronized内部的共享变量,每次读取数据的时候被强制从主内存读取最新的数据。

2、monitorexit指令也具有Store屏障的作用,也就是让synchronized代码块内的共享变量,如果数据有变更的,强制刷新回主内存。

数据修改之后立即刷新回主内存,其他线程进入synchronized代码块后,使用共享变量的时候强制读取主内存的数据。

有序性

同样的,synchronized也是通过monitorenter、monitorexit指令嵌入上面的内存屏障,既具有加锁、释放锁的功能,同时也具有 内存屏障 的功能

文章来源:智云一二三科技

文章标题:java中synchronized关键字基础-1

文章地址:https://www.zhihuclub.com/200299.shtml

关于作者: 智云科技

热门文章

网站地图