我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的:

public class SynchronizedDemo{
public void method(){
synchronized(this){
System.out.println("Method 1 start");
}
}
}

反编译

可以看到有monitorenter和monitorexit两条指令

关于这两条指令的描述我们参考JVM规范:

monitorenter:

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

大概意思是说:

每个对象都有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2.如果线程已经占有该monitor,只是重新计入,则进入monitor的进入数加1.

2.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

这段话的大概意思是说:

执行monitorexit的线程必须是objectref说对应的monitor的所有者。

执行指令时,monitor的进入数减1.如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者,奇特被这个monitor阻塞的线程可以尝试去获取这个monitor的所有权。

通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等党阀也是依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

方法的同步没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步:当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功后才能执行该方法,方法执行完后在释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步上是一种隐私的方式来实现,无需通过字节码来完成。

Synchronized的基本使用

Synchronized是java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:

(1)确保线程互斥的访问同步代码

(2)保证共享变量的修改能够急事课件

(3)有效解决重拍问题。

从语法上将,Synchronized总共有三种用法:

(1)修饰普通方法

(2)修饰静态方法

(3)修饰代码块

接下来我就通过几个立在程序来说明一下这三种方式:

1.没有同步的情况:

public class SynchronizedTest {

    public void method1(){
System.out.println("Method 1 start");
try{
System.out.println("Method 1 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 1 end");
} public void method2(){
System.out.println("Method 2 start");
try{
System.out.println("Method 2 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 2 end");
} public static void main(String[] args) {
// TODO Auto-generated method stub
final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable(){
@Override
public void run(){
test.method1();
}
}).start(); new Thread(new Runnable(){
@Override
public void run(){
test.method2();
}
}).start();
} }

执行结果为

Method 1 start
Method 1 execute
Method 2 start
Method 2 execute
Method 2 end
Method 1 end

2.对普通方法同步

public class SynchronizedTest {

    public synchronized void method1(){
System.out.println("Method 1 start");
try{
System.out.println("Method 1 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 1 end");
} public synchronized void method2(){
System.out.println("Method 2 start");
try{
System.out.println("Method 2 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 2 end");
} public static void main(String[] args) {
// TODO Auto-generated method stub
final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable(){
@Override
public void run(){
test.method1();
}
}).start(); new Thread(new Runnable(){
@Override
public void run(){
test.method2();
}
}).start();
} }

执行结果为:

Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end

3.静态方法(类)同步

public class SynchronizedTest {

    public static synchronized void method1(){
System.out.println("Method 1 start");
try{
System.out.println("Method 1 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 1 end");
} public static synchronized void method2(){
System.out.println("Method 2 start");
try{
System.out.println("Method 2 execute");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 2 end");
} public static void main(String[] args) {
// TODO Auto-generated method stub
final SynchronizedTest test = new SynchronizedTest();
final SynchronizedTest test2 = new SynchronizedTest(); new Thread(new Runnable(){
@Override
public void run(){
test.method1();
}
}).start(); new Thread(new Runnable(){
@Override
public void run(){
test2.method2();
}
}).start();
} }

执行结果如下,对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。

Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end

4、代码块同步

public class SynchronizedTest {

    public void method1(){
System.out.println("Method 1 start");
try{
synchronized (this) {
System.out.println("Method 1 execute");
Thread.sleep(3000);
}
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 1 end");
} public void method2(){
System.out.println("Method 2 start");
try{
synchronized (this) {
System.out.println("Method 2 execute");
Thread.sleep(3000);
}
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Method 2 end");
} public static void main(String[] args) {
// TODO Auto-generated method stub
final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable(){
@Override
public void run(){
test.method1();
}
}).start(); new Thread(new Runnable(){
@Override
public void run(){
test.method2();
}
}).start();
} }

执行结果如下,虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。

Method 1 start
Method 1 execute
Method 2 start
Method 1 end
Method 2 execute
Method 2 end

Java Synchronized的原理的更多相关文章

  1. Java synchronized实现原理总结和偏量锁、轻量锁、重量锁、自旋锁

    synchronized实现同步的基础:Java中的每一个对象都可以作为锁.具体表现为以下3种形式. 对于普通同步方法,锁是当前实例对象(this). 对于静态同步方法,锁是当前类的Class对象. ...

  2. java synchronized 的原理。

    synchronized的作用大概分为三种: 1.确保多线程互斥的访问多线程代码.2.保证变量的可见性.3.防止指令重排序. 那么synchronized 是如何实现这些功能的. public cla ...

  3. Java synchronized的原理解析

    开始 类有一个特性叫封装,如果一个类,所有的field都是private的,而且没有任何的method,那么这个类就像是四面围墙+天罗地网,没有门.看起来就是一个封闭的箱子,外面的进不来,里面的出不去 ...

  4. java synchronized 关键字原理

    Synchronized 关键字是解决并发问题常用解决方案,有以下三种使用方式: 同步普通方法,锁的是当前对象.同步静态方法,锁的是当前 Class 对象.同步块,锁的是 {} 中的对象. 实现原理: ...

  5. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

  6. Java并发编程原理与实战九:synchronized的原理与使用

    一.理论层面 内置锁与互斥锁 修饰普通方法.修饰静态方法.修饰代码块 package com.roocon.thread.t3; public class Sequence { private sta ...

  7. Java程序员必会Synchronized底层原理剖析

    synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用. 但不可否认的是synchr ...

  8. Java精通并发-synchronized关键字原理详解

    关于synchronized关键字原理其实在当时JVM的学习[https://www.cnblogs.com/webor2006/p/9595300.html]中已经剖析过了,这里从研究并发专题的角度 ...

  9. Java多线程和并发(八),synchronized底层原理

    目录 1.对象头(Mark Word) 2.对象自带的锁(Monitor) 3.自旋锁和自适应自旋锁 4.偏向锁 5.轻量级锁 6.偏向锁,轻量级锁,重量级锁联系 八.synchronized底层原理 ...

随机推荐

  1. python 3 mysql 单表查询

    python 3 mysql 单表查询 1.准备表 company.employee 员工id id int 姓名 emp_name varchar 性别 sex enum 年龄 age int 入职 ...

  2. Python导出数据生成excel报表

    #_*_coding:utf-8_*_ import MySQLdb import xlwt from datetime import datetime def get_data(sql): # 创建 ...

  3. /etc/init.d目录和/etc/rc.local脚本

    一.关于/etc/init.d 如果你使用过Linux系统,那么你一定听说过init.d目录.这个目录到底是干嘛的呢?它归根结底只做了一件事情,但这件事情非同小可,是为整个系统做的,因此它非常重要.i ...

  4. Smarty 的安装

    1.下载Smarty包可以从官方站点下载:http://smarty.php.net/ 2.解压缩Smarty包解压后的文件夹重命名为Smarty,放置在C:\Apache2\include下 3.修 ...

  5. Python的标准GUI:Tkinter的组件

    Label组件: Label组件用于显示文本和图像,并且使用双重缓冲 用法: 使用Label组件可以指定想要显示的内容(文本.位图或者图片): from tkinter import * master ...

  6. ios app被自己从应用商店下架后可以再恢復上架吗

    好像没有企业能阻挡苹果的下架决定,毕竟这是它的地盘.不管是已经恢复上架的百度.腾讯.优酷.人人游戏,还是至今没有下文的360.金山和PPS,也不管这些企业在中国乃至全球互联网行业的地位如何,下架原因只 ...

  7. BZOJ 3296 [USACO2011 Open] Learning Languages:并查集

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3296 题意: 农夫约翰的N(2 <= N <= 10,000)头奶牛,编号为1 ...

  8. php设计模式课程---3、为什么会有抽象工厂方法

    php设计模式课程---3.为什么会有抽象工厂方法 一.总结 一句话总结: 解决简单工厂方法增加新选择时无法满足面向对象编程中的开闭原则问题 1.什么是面向对象编程中的开闭原则? 应该对类的增加开放, ...

  9. 201621123014《Java程序设计》第三周学习总结

    <Java程序设计>第三周实验报告 1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识点组织起来.请使用工具画出本周学习到的知识 ...

  10. BeanUtils介绍及使用

    JavaBeans事实上有三层含义.首先,JavaBeans是一种规范,一种在Java(包括JSP)中可重复使用的Java组件的技术规范,也可以说成我们常说的接口.其次,JavaBeans是一个Jav ...