整理一下synchronized关键字相关的知识点。

在多线程并发编程中synchronized扮演着相当重要的角色,synchronized关键字是用来控制线程同步的,可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,保证一个线程的变化(主要是共享变量的变化)被其他线程所看到,即保证可见性,可以替代volatile。

1、Synchronized具体表现形式

  synchronized的实现和对象锁有关,Java中的每一个对象都可以作为锁,具体表现为以下三种形式:

  • 修饰普通方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁;

public synchronized void method1()

{

// todo

}

  • 修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁;

public static synchronized void method2()

{

// todo

}

  • 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。锁是Synchonized括号里配置的对象。配置的对象分为以下三种情况:

1、实例对象

synchronized(this) {

// todo

}

2、类对象

synchronized(Demo.class) {

// todo

}

3、任意实例对象Object

String lock = “”;

synchronized(lock) {

// todo

}

需要注意的是:如果锁的是类对象的话,尽管new多个实例对象,但他们仍然是属于同一个类依然会被锁住,即线程之间保证同步关系。

2、Synchonized实现原理

  JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。同步代码块为显示同步,使用monitorenter 和monitorexit指令实现。同步方法为隐式同步,由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现。

public class SynchronizedDemo {

public static void main(String[] args) {

synchronized (SynchronizedDemo.class) { }

}

public synchronized void method() { }

}

先看下上面这段代码,包含一个同步代码块和一个同步方法。通过javap –v 命令查看编译后的class文件。

  先来看下main方法,上图中第4、6、12句命令就是添加synchronized之后生成的。执行同步代码块之前要先执行monitorenter指令,退出和异常的时候执行monitorexit指令。其关键就是必须要对对象的监视器monitor进行获取,当线程获取monitor后才能继续往下执行,否则将会被阻塞在同步块和同步方法的入口处,同时添加到一个同步队列中。每个对象都存在着一个 monitor 与之对应,当 monitor 被线程持有后,它就处于锁定状态,其他线程不能访问。

  从上图可以看出,method同步方法没有看到monitorenter和monitorexit指令,而是通过ACC_SYNCHRONIZED标识指明method是一个同步方法。方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor,然后再执行方法,方法完成(无论是正常完成还是非正常完成)时释放monitor。Synchronized先天具有重入性,在同一锁程中,线程不需要再次获取同一把锁。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一。

对象,对象监视器,同步队列以及执行线程状态之间的关系如下图:

3、Java对象头

java对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。如下:

实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。

填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。

对象头:synchronized用的锁是存在Java对象头里的,主要结构是Mark Word 和 Class Metadata Address。当对象是数组时,会多一个Array length来存储数组的长度。

  • Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。32位JVM的Mark Word默认存储结构如下:

  • Class Metadata Address存储了类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例。

对象头的信息与对象自身定义的数据是没有关系的,在运行时,Mark Word里存储的数据会随着锁标志位的变化而变化。除了默认存储结构,还有可能变化成以下结构:

这部分照搬了《Java并发编程的艺术》书中的一段,这块的东西只是简单的过了一遍。

4、synchronized的优化

  Java 6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,锁可以升级但不能降级。

Java并发编程之synchronized关键字的更多相关文章

  1. Java 多线程并发编程之 Synchronized 关键字

    synchronized 关键字解析 同步锁依赖于对象,每个对象都有一个同步锁. 现有一成员变量 Test,当线程 A 调用 Test 的 synchronized 方法,线程 A 获得 Test 的 ...

  2. Java并发编程之volatile关键字解析

    一内存模型的相关概念 二并发编程中的三个概念 三Java内存模型 四深入剖析volatile关键字 五使用volatile关键字的场景 volatile这个关键字可能很多朋友都听说过,或许也都用过.在 ...

  3. Java并发编程之volatile关键字

    大概是因为项目.业务的原因,工作上几乎还没有使用过多线程相关的功能,相关知识差不多都忘了,所以最近补一下基础. volatile用来修饰共享变量,volatile变量具有 synchronized 的 ...

  4. 并发编程之synchronized关键字

    synchronized关键字 synchronized关键字最主要的三种使用方式的总结 1.修饰实例方法,作用于当前对象实例加锁,进入同步代码块前要获得当前对象实例的锁 2.修饰静态方法,作用于当前 ...

  5. Java 并发编程之volatile关键字解析

    摘录 1. 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执 ...

  6. Java并发编程之synchronized

    在Java编程中,为了保证线程安全,有3种不同的思路1.互斥同步:包括synchronized和lock等. 2.非阻塞同步:如AtomicInteger的increaseAndGet()方法等. 3 ...

  7. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  8. Java并发编程之CAS二源码追根溯源

    Java并发编程之CAS二源码追根溯源 在上一篇文章中,我们知道了什么是CAS以及CAS的执行流程,在本篇文章中,我们将跟着源码一步一步的查看CAS最底层实现原理. 本篇是<凯哥(凯哥Java: ...

  9. Java并发编程之CAS第一篇-什么是CAS

    Java并发编程之CAS第一篇-什么是CAS 通过前面几篇的学习,我们对并发编程两个高频知识点了解了其中的一个—volatitl.从这一篇文章开始,我们将要学习另一个知识点—CAS.本篇是<凯哥 ...

随机推荐

  1. Linux下Mysql安装(RPM安装)

    1. 首先检查机器里是否已经存在MySQL $ rpm -qa | grep mysql 2. 去官网下载相应的rpm包:https://dev.mysql.com/downloads/mysql/ ...

  2. thinkphp 视图(一)

    视图 View <?php namespace app\index\controller; class Index{ public function index(){ return view() ...

  3. ES6 Rest参数

    Rest参数接收函数的多余参数,组成一个数组,放在形参的最后,形式如下: function func(a, b, ...theArgs) { // ... } rest参数只包括那些没有给出名称的参数 ...

  4. RIDE 接口自动化请求体参数中文时报错:“UnicodeDecodeError: 'ascii' codec can't decode byte 0xd7 in position 9......”

    在进行robotframework  接口自动化,在请求体参数中输入中文会报以下错误: UnicodeDecodeError: 'ascii' codec can't decode byte 0xd7 ...

  5. SQLite3命令操作大全

    SQLite3命令操作大全 SQLite库包含一个名字叫做sqlite3的命令行,它可以让用户手工输入并执行面向SQLite数据库的SQL命令.本文档提供一个样使用sqlite3的简要说明. 一.ql ...

  6. web版ssh的使用

    一.web_ssh版本安装使用 web_ssh源码:https://github.com/shellinabox/shellinabox 1)安装依赖包 yum install git openssl ...

  7. jedis set 的四个重载方法(byte[]的四个自动忽略)

    方法定义如下: 1.String set(String key, String value) 2.String set(String key, String value, String nxxx) 3 ...

  8. Tomcat架构解析(五)-----Tomcat的类加载机制

    类加载器就是根据类的全限定名(例如com.ty.xxx.xxx)来获取此类的二进制字节流的代码模块,从而程序可以自己去获取到相关的类. 一.java中的类加载器   1.类加载器类别 java中的类加 ...

  9. 小程序json字符串转为对象

    小程序里json字符串转为对象使用JSON.parse()方法转变无效, 看报错提示有单引号“ ' ” 因为单引号而无效, 将单引号全改双引号即可. 报错如下: VM11050:1 thirdScri ...

  10. openxml excel封装类

    public class ExcelUntity { #region property /// <summary> /// excel文档(相当于excel程序) /// </sum ...