一、synchronized的使用
(一)、synchronized同步方法
1. “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题。
2. 如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题。
3. synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。因此在多线程访问同一个对象时,哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁lock,其他线程只能呈等待状态。
但如果多个线程访问多个对象,则JVM会创建多个锁。
4. 调用关键字synchronized声明的方法一定是排队运行的。另外只有共享资源的读写访问才需要同步化,如果不是共享资源,就没有同步化的必要。
5. 脏读。当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized关键字的同步方法。
6. synchronized锁重入。使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时,是可以再次得到该对象的锁的。
7. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
8. 同步不可以继承。如果父类方法使用了关键字synchronized,那么子类方法不会继承synchronized,如果子类需要同步,还需要在子类方法中添加synchronized。

(二)、synchronized同步代码块
1. 同一个方法中,不在synchronized块中的就是异步执行,在synchronized块中的就是同步执行。
2. 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这是因为synchronized使用的是“对象监视器”。
3. 和synchronized方法一样,synchronized(this)代码块也是锁定当前对象。
4. java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个任意对象大多数是实例变量及方法的参数,使用格式为:synchronized(非this对象x),此时将x对象本身作为“对象监视器”
锁非this对象的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,可以大大提高运行效率。
5. synchronized还可以应用在static静态方法上,如果这样写,就是对当前的*.java文件对应的class类进行加锁。
跟synchronized加到非static方法上的区别:synchronized加到static静态方法上,是给Class类上锁;而synchronized加到非static静态方法上,是给对象上锁。
6. 由于JVM具有String常量池缓存的功能,因此在大多数情况下,同步synchronized代码块都不使用String作为锁对象:synchronized(string),而改用其他的,比如new Object实例化一个Object对象,但它并不放入缓存中。

以上方法的具体代码例子可以参考《java多线程编程核心技术》

二、synchronized的实现原理

目前在Java中存在两种锁机制:synchronized和Lock
synchronized实现同步的基础:Java中每一个对象都可以作为锁,具体表现为以下3种方式:(Synchronized锁, 锁住的是对象。)
1. 对于普通同步方法,锁是当前实例对象
2. 对于静态同步方法,锁是当前类的class对象
3. 对于同步方法块,锁是synchronized括号里配置的对象
synchronized用的锁是存在java对象头里的。重量级锁、轻量级锁和偏向锁之间的转换:

Synchronized同步块和同步方法在实现上的区别:

在下面的例子中,使用了同步块和同步方法,通过使用javap工具查看生成的class文件信息,来分析synchronized关键字的实现细节。

public class Synchronized {
public static void main(String[] args) {
// 对Synchronized Class对象进行加锁
synchronized (Synchronized.class) { }
// 静态同步方法,对Synchronized Class对象进行加锁
m();
} public static synchronized void m() {
}
}

在synchronized.class的同级目录下执行:javap -v synchronized.class,部分输出如下:

说明:

  public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC //方法修饰符,表示public static
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class testnew/Synchronized
2: dup
3: monitorenter // monitorenter:监视器进入,获取锁
4: monitorexit // monitorexit: 监视器退出,释放锁
5: invokestatic #16 // Method m:()V
8: return public static synchronized void m();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //方法修饰符,表示public static synchronized
Code:
stack=0, locals=0, args_size=0
0: return

对于同步块的实现使用了monitorenter和monitorexit指令,而同步方法则是依靠方法修饰符上的ACC_SYNCHRONIZED来完成的。无论采用哪种方法,其本质是对一个对象的监视器(monitor)进行获取,而这个获取过程是排他的,也就是同一个时刻只能有一个线程获取到由synchronized所保护对象的监视器。

任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。

下图描述了对象、对象的监视器、同步队列和执行线程之间的关系。

可以看出,任意线程对Object(Object由synchronized保护)的方法,首先要获得Object的监视器。如果获取失败,线程进入同步队列,线程状态变为BLOCKED。当访问Object的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列总的线程,使其重新尝试对监视器的获取。

相关阅读:java中的锁分类

参考:

《Java并发编程的艺术》

Java中的锁机制 synchronized & 偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景 & AtomicReference

对synchronized的一点理解的更多相关文章

  1. java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解

    synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...

  2. opencv笔记5:频域和空域的一点理解

    time:2015年10月06日 星期二 12时14分51秒 # opencv笔记5:频域和空域的一点理解 空间域和频率域 傅立叶变换是f(t)乘以正弦项的展开,正弦项的频率由u(其实是miu)的值决 ...

  3. 对socket的一点理解笔记

    需要学web service,但是在视频中讲解到了socket套接字编程.以前貌似课上老师有提过,只是没用到也感觉乏味.现在遇到,自己看了些博客和资料.记录一点理解,不知正确与否. 首先说这个名字,叫 ...

  4. iOS 的一点理解(一) 代理delegate

    做了一年的iOS,想记录自己对知识点的一点理解. 第一篇,想记录一下iOS中delegate(委托,也有人称作代理)的理解吧. 故名思议,delegate就是代理的含义, 一件事情自己不方便做,然后交 ...

  5. 关于web开发的一点理解

    对于web开发上的一点理解 1 宏观上的一点理解 网页从请求第地址 到获得页面的过程:从客户端(浏览器)通过地址 从soket把请求报文封装发往服务端   服务端通过解析报文并处理报文最后把处理的结果 ...

  6. angular.js的一点理解

    对angular.js的一点理解 2015-01-14 13:18 by MrGeorgeZhao, 317 阅读, 4 评论, 收藏, 编辑 最近一直在学习angular.js.不得不说和jquer ...

  7. RxSwift 入坑好多天 - 终于有了一点理解

    一.前言 江湖上都在说现在就要赶紧学 swift 了,即将是 swift 的天下了.在 api 变化不大的情况下,swift 作为一门新的语言,集众家之所长,普通编码确实比 oc 要好用的多了 老早就 ...

  8. rt-thread中动态内存分配之小内存管理模块方法的一点理解

    @2019-01-18 [小记] rt-thread中动态内存分配之小内存管理模块方法的一点理解 > 内存初始化后的布局示意 lfree指向内存空闲区首地址 /** * @ingroup Sys ...

  9. rt-thread中软件定时器组件超时界限的一点理解

    @2019-01-15 [小记] 对 rt-thread 中的软件定时器组件中超时界限的一点理解 rt_thread_timer_entry(void *parameter)函数中if ((next_ ...

随机推荐

  1. python json.dumps中文乱码

    json.dumps在默认情况下,对于非ascii字符生成的是相对应的字符编码,而非原始字符,例如: >>> import json>>> js = json.lo ...

  2. renren_fast性能测试平台的安装部署

    1.从GitHub下载源码: https://github.com/zyanycall/stressTestPlatform git clone https://github.com/zyanycal ...

  3. java学习(四)修饰符、运算符、循环结构、分支结构

    修饰符 一般是放在定义类,方法,变量的最前端 访问控制修饰符 修饰符 当前类 同一包内 子孙类 其他包 public Y Y Y Y protected Y Y Y N default Y Y N N ...

  4. linux/Ubuntu系统上安装mysql数据库(附图详解)

    在前面的文章中,我已经分享了如何在Ubuntu系统中安装以及搭建java开发环境,那么当我们需要跟数据打交道的时候,那么就需要在ubuntu系统中安装一个数据库了,那么废话就不多说了,我们这里主要是分 ...

  5. 32bit 天堂2服务端多机负载

    第一步..先确定..单机架设成功.. 第二步..复制整个服务器端文件到第2个服务器 第3步.. 将你C:\Program Files\Common Files\ODBC\Data Sources 中的 ...

  6. Hadoop源码阅读环境搭建(IDEA)

    拿到一份Hadoop源码之后,经常关注的两件事情就是 1.怎么阅读?涉及IDEA和Eclipse工程搭建.IDEA搭建,选择源码,逐步导入即可:Eclipse可以选择后台生成工程,也可以选择IDE导入 ...

  7. vue+webpack前端开发项目的安装方法

    安装前,需要进行node.npm检测,查看是否已有安装node.npm环境: 操作方法:Windows+R 调出运行框,输入cmd 调出命令框:分别输入node -v 回车(查看node版本) npm ...

  8. Oracle VM VirtualBox 无法卸载 更新 和修复

    好久没更新Oracle VM VirtualBox 突然发现不能更新了 提示要某个msi文件,回想起来好像是被某个清理垃圾的软件清理掉了. 于是根据提示的版本号网上搜了种子又把安装包下载回来 在命令行 ...

  9. 第十一次ScrumMeeting博客

    第十一次ScrumMeeting博客 本次会议于11月29日(三)22时整在3公寓725房间召开,持续30分钟. 与会人员:刘畅.辛德泰张安澜.赵奕.方科栋. 1. 每个人的工作(有Issue的内容和 ...

  10. ORM PHP 学习记录

    ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射.为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却 ...