加不加 synchronized 有什么区别?

synchronized 作为悲观锁,锁住了什么?

之前 2 篇文章我们已经知道 synchronized 的使用方法以及锁的内容(实例对象和Class类对象),这已经涵盖了这个关键字的基本内容了,今天这篇想介绍一下另一种写法,就是同步代码块,它实现了更细粒度的同步方式。下面来见分晓。

先给大家介绍一下同步代码块怎么写,大体的代码框架是这样:

synchronized(xxx) {

}

xxx 可以是 this 或者 Object 或者 xxx.class,下面我们就根据这 3 种不同加锁方式进行展开讨论。

this

表示的是锁住当前对象,和原来使用同步实例方式一样,锁住了当前的对象。

public class SynchronizedCodeTest {

    public static void main(String[] args) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
for (int i = 0; i < 5; i ++) {
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCode();
});
thread.start();
}
} int count = 0;
public void testSynchronizedCode() {
System.out.printf("%s-testSynchronizedCode-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (this) {
System.out.printf("%s-testSynchronizedCode-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchronizedCode-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchronizedCode-end-count=%s\n", Thread.currentThread().getName(), count);
} }

运行结果:

我们主要关注红色框和蓝色框的这部分结果,红色框里面是同步块的代码,线程之间是互斥的,但是蓝色框中Thread-0在执行同步块的过程中,其他线程非同步块也在执行,这里说明了锁的粒度确实变小了,变成了方法里面的同步块代码之间互斥,非同步块代码不互斥,count 的值最终是 5,说明到执行到同步块时,同一时刻只有一个线程在执行。

我们再写个测试代码,看一看 synchronized(this) 是锁住什么?

public class SynchronizedCodeTest {

    public static void main(String[] args) {
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCode();
});
thread.start();
}
}
}

运行结果:

观察到红色框里面就可以发现,这时候同步块不起效果了,并且 count 最终都是 1,证明 synchronized(this) 锁住的是当前的对象,和 public synchronized void testSynchronizedMethod() 一样。

Object

同步代码块带来了灵活性,它不再只是锁住当前对象了,可以锁住任何我们创建的对象,下面就来看看。


public class SynchronizedCodeTest { public static void main(String[] args) {
Object lock = new Object();
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
for (int i = 0; i < 5; i ++) {
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchroniedLock();
});
thread.start();
}
} int count = 0; Object lock = null;
public SynchronizedCodeTest(Object lock) {
this.lock = lock;
} public void testSynchroniedLock() {
System.out.printf("%s-testSynchroniedLock-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (lock) {
System.out.printf("%s-testSynchroniedLock-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchroniedLock-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchroniedLock-end-count=%s\n", Thread.currentThread().getName(), count);
} }

运行结果:

这段代码,我们创建了一个 lock 对象,作为参数传入到 synchronizedCodeTest 对象里,我们看到结果里面,5 个线程在同步块代码里是串行执行的,count 最终也得到结果是 5。这段代码没有看出锁对象带来的灵活性,下面再看一个例子,把测试代码稍微改一下,让每个线程都有自己的 synchronizedCodeTest 对象。

public static void main(String[] args) {
Object lock = new Object();
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchroniedLock();
});
thread.start();
}
}

运行结果:

结果我们发现,虽然我们为每个线程创建一个 synchronizedCodeTest 对象,但是不管怎么运行,5 个线程的同步代码块都是串行执行的,原因在于,我们只创建了一个 lock 对象,这 5 个 synchronizedCodeTest 的 lock 对象都是同一个,因此竞争资源是同一个,才出现这种情况。看是不是比同步方法灵活多了,上一篇中,我们要让多个实例同步执行,我们需要使用静态同步方法,现在不用了,使用同步代码块就可以,只需要满足锁住同一个实例对象就行。

另外,这个例子的结果是每个实例的 count 最终都为 1,这是因为每个 synchronizedCodeTest 对象都有自己独立的变量 count,所以线程之间互不影响。

xxx.class

再来看看最后一种代码块锁 Class 类,这和 public static synchronized testSynchronizedStatic() 的作用是一样的,区别就只是代码块的锁范围可变。我们直接看看代码例子。

public class SynchronizedCodeTest {

    public static void main(String[] args) {
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCodeClass();
});
thread.start();
}
} int count = 0;
public void testSynchronizedCodeClass() {
System.out.printf("%s-testSynchronizedCodeClass-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (SynchronizedCodeTest.class) {
System.out.printf("%s-testSynchronizedCodeClass-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchronizedCodeClass-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchronizedCodeClass-end-count=%s\n", Thread.currentThread().getName(), count);
} }

运行结果:

每个线程都有自己的实例,但是锁住 Class 会使每个线程实例对象的同步块都是串行执行,这个结果和上面的多个实例锁住同一个 Object 对象的结果是一样的。我个人更倾向于使用锁同一个 Object 对象,而不是锁 Class 类对象。

总结

这篇介绍了synchronizd 代码块的 3 种使用方式,并详细介绍了各自的使用方式和区别。简单的列个表。

类型 使用方式 锁作用范围
this synchronized(this){} 锁住当前的实例对象
object synchronized(lock){} 锁住其他实例对象,比较灵活
xxx.class synchronized(xxx.class){} 锁住 Class 对象

总共用了 3 篇文章来给大家介绍 synchronized 的具体用法,主要是因为之前有些文章一下子就进入 Java 源代码和 JVM 源码,让不少朋友感觉有点吃力,甚至有朋友一看标题就不进来看看了,所以这次先介绍一些比较简单的用法,让大家先了解了解,后面几篇会比较深入 synchronized 原理性的文章,希望大家也能看看,会有非常大的收获的。

原创不易,大家多帮忙转发,点在看。

synchronized 作为悲观锁,锁住了什么?

加不加 synchronized 有什么区别?

从 JVM 视角看看 Java 守护线程

写了那么多年 Java 代码,终于 debug 到 JVM 了

全网最新最简单的 openjdk13 代码编译

了解Java线程优先级,更要知道对应操作系统的优先级,不然会踩坑

线程最最基础的知识

老板叫你别阻塞了

吃个快餐都能学到串行、并行、并发

泡一杯茶,学一学同异步

进程知多少?

设计模式看了又忘,忘了又看?

后台回复『设计模式』可以获取《一故事一设计模式》电子书

觉得文章有用帮忙转发&点赞,多谢朋友们!

synchronized 代码块怎么用的更多相关文章

  1. 多线程学习-基础( 十)一个synchronized(){/*代码块*/}简单案例分析

    一.提出疑惑 上一篇文章中,分析了synchronized关键字的用法.但是好像遗漏了一种情况. 那就是: synchronized(obj){/*同步块代码*/} 一般有以下几种情况: (1)syn ...

  2. (转载)synchronized代码块

    原文:http://blog.csdn.net/luoweifu/article/details/46613015 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)——以 ...

  3. 【面试普通人VS高手系列】讲一下wait和notify这个为什么要在synchronized代码块中?

    一个工作七年的小伙伴,竟然不知道"wait"和"notify"为什么要在Synchronized代码块里面. 好吧,如果屏幕前的你也不知道,请在评论区打上&qu ...

  4. 2016/9/25编写java实验报告时对synchronized(同步代码块)的一些感悟

    通过此次实验,明白了多线程的设置和启动.synchronized代码块的用法.线程的优先级使用方法.知道了那几类资源是线程共享的. 我现在理解的多线程是:实例化一个继承了Thread类或实现了Runn ...

  5. 线程同步 synchronized 同步代码块 同步方法 同步锁

    一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...

  6. 彻底理解线程同步与同步代码块synchronized

    public class Demo { public static synchronized void fun1(){ } public synchronized void fun2(){ } pub ...

  7. synchronized锁机制 之 代码块锁(转)

    synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用 ...

  8. synchronized同步代码块锁释放

    今天发现自己写的线上程序出现数据库不能同步的问题,查看日志已经停止记录,随后使用jstack查看线程的运行状况,发现有个同步线程锁住了. 以下是jstack -l 637  问题线程的内容. &quo ...

  9. 59、synchronized同步代码块

    synchronized同步方法的问题 有些情况下,在方法上面加synchronized同步,会有性能问题.请看下面代码,来计算下两个线程执行的耗时: package com.sutaoyu.Thre ...

随机推荐

  1. windows server 2016 远程桌面mstsc DPI(更改文本、应用和其他项目大小) 设置

    windows server 2016 远程桌面mstsc DPI 设置 在高分辨率机器2K,4K,8K,登入使用window远程桌面mstsc时,登入后虽然分辨率变成了和cilent一样分辨率 但是 ...

  2. Java复合优先于继承

    复合优于继承 继承打破了封装性(子类依赖父类中特定功能的实现细节) 合理的使用继承的情况: 在包内使用 父类专门为继承为设计,并且有很好的文档说明,存在is-a关系 只有当子类真正是父类的子类型时,才 ...

  3. 1012 The Best Rank (25 分)

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  4. flask-宏

    flask-宏 模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,使用宏的时候,参数可以为默认值. ...

  5. C语言 文件操作(四)

    1.fprintf int fprintf(FILE *stream, const char *format, ...) stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流 ...

  6. 汇编刷题:内存 MEM 单元开始存放着 10 个带符号字节数据, 编写完整程序求其中正数、 零和负数的个 数, 并分别将它们存于 PLUS、 ZERO 和 NEGO 3 个单元

    DATA SEGMENT MEM DB 12H,91H,73H,64H,20H,0A5H,0D1H,91H,0A2H,00H PLUS DB 00H ZERO DB 00H NEGO DB 00H D ...

  7. 统计分析_集中趋势and离散程度

    1.数组的集中趋势-如何定义数组的中心 1.1 常用几下几个指标来描述一个数组的集中趋势 均值-算术平均数 . 中位数-将数组升序或降序排列后,位于中间的数. 众数-数组中出现最多的数. 1.2 指标 ...

  8. word多级列表应用

  9. Python设计模式(9)-外观模式

    # /*外观模式:为外界调用提供一个统一的接口,把其他类中需要用到的方法提取# * 出来,由外观类进行调用.然后在调用段实例化外观类,以间接调用需要的# * 方法.这种方式和代理模式有异曲同工之妙.然 ...

  10. tf.nn.bias_add 激活函数

    tf.nn.bias_add(value,bias,data_format=None,name=None) 参数: value:一个Tensor,类型为float,double,int64,int32 ...