synchronized对于java同学肯定都是耳熟能详的必修课了。但是不管对于新手还是老手都有一些容易搞错的点。这里权做一点记录。

锁的是代码还是对象?

同步块一般有两种写法。

1是直接加以方法体上。

public synchronized void incr3(){
i++;
}

2是块小锁粒度的加到代码块

public void incr() throws InterruptedException {
synchronized (SyncIncrDemo.class){
i++;
}
}

不管怎样,锁的都是对象。对于这段代码来说,谁拿到这个锁对象,就能优先执行锁定的代码,且对其它线程互斥。

代码2显示锁定的类对象,那么代码1在方法体上锁对象是谁呢?

debug下打印堆栈信息可以很明显的看到代码块1里面的锁是类的实例对象 locked <0x218> (a com.alpha.data.util.SyncIncrDemo)

Full thread dump

"main@1" prio=5 tid=0x1 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.Thread.join(Thread.java:1252)

at java.lang.Thread.join(Thread.java:1326)

at com.alpha.data.util.SyncIncrDemo.main(SyncIncrDemo.java:59)

"Thread-1@530" prio=5 tid=0xf nid=NA sleeping

java.lang.Thread.State: TIMED_WAITING

at java.lang.Thread.sleep(Thread.java:-1)

at com.alpha.data.util.SyncIncrDemo.incr2(SyncIncrDemo.java:33)

- locked <0x217> (a com.alpha.data.util.SyncIncrDemo)

at com.alpha.data.util.SyncIncrDemo.run(SyncIncrDemo.java:49)

at java.lang.Thread.run(Thread.java:748)

"Thread-0@529" prio=5 tid=0xe nid=NA sleeping

java.lang.Thread.State: TIMED_WAITING

at java.lang.Thread.sleep(Thread.java:-1)

at com.alpha.data.util.SyncIncrDemo.incr2(SyncIncrDemo.java:33)

- locked <0x218> (a com.alpha.data.util.SyncIncrDemo)

at com.alpha.data.util.SyncIncrDemo.run(SyncIncrDemo.java:49)

at java.lang.Thread.run(Thread.java:748)

"Finalizer@533" daemon prio=8 tid=0x3 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)

at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler@534" daemon prio=10 tid=0x2 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.Object.wait(Object.java:502)

at java.lang.ref.Reference.tryHandlePending(Reference.java:191)

at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"Attach Listener@531" daemon prio=5 tid=0x5 nid=NA runnable

java.lang.Thread.State: RUNNABLE

"Signal Dispatcher@532" daemon prio=9 tid=0x4 nid=NA runnable

java.lang.Thread.State: RUNNABLE

对于静态方法呢?

public static synchronized void incr3(){
i++;
}

打印堆栈,可以看到是锁的是类对象 locked <0x20f> (a java.lang.Class)

这也符合预期,因为静态类方法属于类对象,而不是实例对象。

Full thread dump

"Thread-0@529" prio=5 tid=0xe nid=NA waiting for monitor entry

java.lang.Thread.State: BLOCKED

waiting for Thread-1@530 to release lock on <0x20f> (a java.lang.Class)

at com.alpha.data.util.SyncIncrDemo.incr(SyncIncrDemo.java:24)

at com.alpha.data.util.SyncIncrDemo.run(SyncIncrDemo.java:51)

at java.lang.Thread.run(Thread.java:748)

"main@1" prio=5 tid=0x1 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.Thread.join(Thread.java:1252)

at java.lang.Thread.join(Thread.java:1326)

at com.alpha.data.util.SyncIncrDemo.main(SyncIncrDemo.java:61)

"Thread-1@530" prio=5 tid=0xf nid=NA sleeping

java.lang.Thread.State: TIMED_WAITING

blocks Thread-0@529

at java.lang.Thread.sleep(Thread.java:-1)

at com.alpha.data.util.SyncIncrDemo.incr(SyncIncrDemo.java:24)

- locked <0x20f> (a java.lang.Class)

at com.alpha.data.util.SyncIncrDemo.run(SyncIncrDemo.java:51)

at java.lang.Thread.run(Thread.java:748)

"Finalizer@533" daemon prio=8 tid=0x3 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)

at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler@534" daemon prio=10 tid=0x2 nid=NA waiting

java.lang.Thread.State: WAITING

at java.lang.Object.wait(Object.java:-1)

at java.lang.Object.wait(Object.java:502)

at java.lang.ref.Reference.tryHandlePending(Reference.java:191)

at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"Attach Listener@531" daemon prio=5 tid=0x5 nid=NA runnable

java.lang.Thread.State: RUNNABLE

"Signal Dispatcher@532" daemon prio=9 tid=0x4 nid=NA runnable

java.lang.Thread.State: RUNNABLE

所以,对于synchronized代码块,锁对象为显示的锁定对象;synchronized方法分为静态方法和非静态方法,对于静态方法,锁对象为该class对,否则属于该class的实例对象。

public static synchronized void incr3(){
i++;
}
等同于 public synchronized void incr3() throws InterruptedException {
synchronized (SyncIncrDemo.class){
Thread.sleep(100000);
}
i++;
}
public synchronized void incr3(){
i++;
}
等同于 public synchronized void incr3() throws InterruptedException {
synchronized (this){
Thread.sleep(100000);
}
i++;
}

对于相同的竞态场景,必须持相同的锁对象。通俗的讲,一个门只能有一把锁。

为什么会有这样的问题。这个问题对于新手来讲有点饶,对于有些人来讲是废话。

源于看到一篇博文,大概是有一个Integer类型的共享变量,多线程并发对其操作,在同步块里对于操作。伪代码

Integer count = 0;

public void add(){
synchronized(count){
count++;
}
}

这样子很明显是达不到预期结果的。Integer在超出-128到127范围以后,每一个对象都是一个新的实例对象。换句话说,这里想要达到N多人同时想要通过一个通道,通道门上有多把锁,很多人可以同时拿到锁进而并发地通过。达不到预期目的。

可能有人会不以为然,对代码块加同步锁肯定不能用Integer这种对象啊,这只能是新手犯的错,用this呢?

前面说到,使用this锁对象为当前class的实例对象,在spring默认单例的情况下,肯定没有问题,但如果某位同事在某天在某种需求场景下改为了多例呢?

很明显的,多例情况下,就是同一个门多把锁了。

所以这里是需要注意的点。

我觉得最安全最健壮的方法是不要在方法上加同步块,一律使用类对象作为锁。

synchronized(A.class){
}

synchronized就一定安全吗

从所周知,

synchronized能保证原子性,可见性,有序性,

volatile能保证可见性和有序性,防止指令重排。

那么synchronized是不是就一定能保证线程安全呢?共享变量就可以不用volatile了呢?

我觉得答案是否定的。哪怕是在synchronized内部,如果有共享变量,必须强制使用volatile,这是一个好的编码习惯。

synchronized 的有序性是持有相同锁的两个同步块只能串行的进入,即被加锁的内容要按照顺序被多个线程执行,但是其内部的同步代码还是会发生重排序,使块与块之间有序可见。

volatile的有序性是通过插入内存屏障来保证指令按照顺序执行。不会存在后面的指令跑到前面的指令之前来执行。是保证编译器优化的时候不会让指令乱序。

synchronized 是不能保证指令重排的。

【工作随手记】并发之synchronized的更多相关文章

  1. 并发之synchronized关键字的应用

    并发之synchronized关键字的应用 synchronized关键字理论基础 前两章我们学习了下java内存模型的相关知识, 现在我们来讲讲逢并发必出现的synchronized关键字. 作用 ...

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

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

  3. Java并发之synchronized

    Java多线程同步关键词是常用的多线程同步手段.它可以修饰静态类方法,实例方法,或代码块.修饰static静态方法时是对整个类加锁. 一.实现原理 在JVM中对象内存分三块区域,对象头.实例数据.对齐 ...

  4. Java并发之synchronized关键字深度解析(二)

    前言 本文继续[Java并发之synchronized关键字深度解析(一)]一文而来,着重介绍synchronized几种锁的特性. 一.对象头结构及锁状态标识 synchronized关键字是如何实 ...

  5. Java并发之synchronized关键字

         上篇文章我们主要介绍了并发的基本思想以及线程的基本知识,通过多线程我们可以实现对计算机资源的充分利用,但是在最后我们也说明了多线程给程序带来的两种典型的问题,针对它们,synchronize ...

  6. Java并发之synchronized关键字深度解析(一)

    前言 近期研读路神之绝世武学,徜徉于浩瀚无垠知识之海洋,偶有攫取吉光片羽,惶恐未领略其精髓即隐入岁月深处,遂急忙记录一二,顺备来日吹cow之谈资.本小系列为并发之亲儿子-独臂狂侠synchronize ...

  7. 《提升能力,涨薪可待》—Java并发之Synchronized

    Synchronized简介 线程安全是并发编程中的至关重要的,造成线程安全问题的主要原因: 临界资源, 存在共享数据 多线程共同操作共享数据 而Java关键字synchronized,为多线程场景下 ...

  8. K60平台智能车开发工作随手记

    (图片仅为示例,并不一定固定为这种造型) 第十二届全国大学生智能汽车竞赛有一个分项是光电四轮车的竞速(任务A),Seven她们组采购到的配件使用了freescale Crotex-M4内核的CPU,T ...

  9. Java并发之synchronized深入

    一句话总结synchronized: JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质. 一.synchron ...

  10. Java并发之synchronized使用

    synchronized,是Java语言的关键字,读['siŋkrənaizd],当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.Java为何要使用sy ...

随机推荐

  1. IDEA: 如何导入项目模块 以及 将 Java程序打包 JAR 详细步骤

    IDEA: 如何导入项目模块 以及 将 Java程序打包 JAR 详细步骤 . @ 目录 IDEA: 如何导入项目模块 以及 将 Java程序打包 JAR 详细步骤 IDEA 导入项目模块 Modul ...

  2. JVM Dump分析

    Thread Dump介绍 Thread Dump是非常有用的诊断 Java应用问题的工具.每一个 Java虚拟机都有及时生成所有线程在某一点状态的 thread-dump的能力,虽然各个 Java虚 ...

  3. unable to find Qt5Core.dll on PATH(已解决,超简单)

    不久之前我在引用PyQt5库的时候总是出现unable to find Qt5Core.dll on PATH的错误,错误如下: 百度上都是说什么打包的时候出错,然后加上一句话,我试过以后也不行,后来 ...

  4. Vue 相关整理

    一 谈谈对 keep-alive 的了解? keep-alive 是Vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,其有以下特性: * 一般结合路由和动态组件一起使用,用于缓存组件: ...

  5. ACM-NEFUOJ-P210畅通工程并查集

    题目:我已经明示到这个程度了你还不用并查集? #include<bits/stdc++.h> using namespace std; const int MAXN=1010; int F ...

  6. 本地Navicat无法连接服务器mysql8.0

    本地Navicat无法连接服务器mysql8.0 原因: mysql未开启远程连接权限 navivat与mysql密码加密不一致,需一致加密规则 允许远程连接  use msyql; // 1.先查询 ...

  7. R语言网络数据爬虫之三个问题

    现在大家对爬虫的兴趣不断高涨,R和PYTHON是两个非常有力的爬虫工具.Python倾向于做大型爬虫,与R相比,语法相对复杂,因此Python爬虫的学习曲线会相对陡峭.对于那些时间宝贵,又想从网上获取 ...

  8. 二进制安装Kubernetes(k8s)IPv4/IPv6双栈 v1.24.0

    二进制安装Kubernetes(k8s) v1.24.0 IPv4/IPv6双栈 介绍 kubernetes二进制安装 1.23.3 和 1.23.4 和 1.23.5 和 1.23.6 和 1.24 ...

  9. [数据库/MySQL]数据库备份与升级:MySQL Percona(RPM) 5.7.24-27 升级到 5.7.31-34

    1 数据库升级方式:RPM包方式升级 [亲测有效] 环境 OS: CENTOS 7 DB: MYSQL 5.7.24-27 1.1 数据库备份 备份以防止升级失败 备份数据库的2个主要方法: 1)用M ...

  10. mysql锁及锁出现总结

    转载请注明出处: 1.按锁粒度分类: 行锁:锁某行数据,锁粒度最小,并发度高:: 行锁是指加锁的时候锁住的是表的某一行或多行记录,多个事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问 ...