synchronized 代码块怎么用

Easter79
• 阅读 515

synchronized 代码块怎么用

加不加 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);
    }

}

运行结果:

synchronized 代码块怎么用

我们主要关注红色框和蓝色框的这部分结果,红色框里面是同步块的代码,线程之间是互斥的,但是蓝色框中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();
        }
    }
}

运行结果:

synchronized 代码块怎么用

观察到红色框里面就可以发现,这时候同步块不起效果了,并且 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);
    }

}

运行结果:

synchronized 代码块怎么用

这段代码,我们创建了一个 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();
    }
}

运行结果:

synchronized 代码块怎么用

结果我们发现,虽然我们为每个线程创建一个 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);
    }

}

运行结果:

synchronized 代码块怎么用

每个线程都有自己的实例,但是锁住 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 代码块怎么用

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
synchronized 的实现原理
!(http://www.liebrother.com/upload/ef1e31512ff14b5da181fc36f9190e16_20171113.jpg)加不加synchronized有什么区别?(https://my.oschina.net/liebrother/blog/4253687)synchronized作为悲观锁,
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k