我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java多线程之synchronized同步代码块详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java多线程之synchronized同步代码块详解

面试题:

1同步方法和同步块,哪种更好?

2.如果同步块内的线程抛出异常会发生什么?

1. 同步方法和同步块,哪种更好?

同步块更好,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:同步的范围越小越好。

对于小的临界区,我们直接在方法声明中设置synchronized同步关键字,可以避免竞态条件的问题。但是对于较大的临界区代码段,为了执行效率,最好将同步方法分为小的临界区代码段。

public class TwoPlus {
    private int num1 = 0;
    private int num2 = 0;
    public synchronized void plus(int val1,int val2){
        this.num1 = num1+val1;
        this.num2 = num2+val2;
    }
}

临界区代码段包含对两个临界区资源的操作,这两个临界区资源分别为sum1和sum2。使用synchronized对plus(int val1,int val2)进行同步保护之后,进入临界区代码段的线程拥有sum1和sum2的操作权,并且是全部占用。一旦线程进入,当线程在操作sum1而没有操作sum2时,也将sum2的操作权白白占用,其他的线程由于没有进入临界区,只能看着sum2被闲置而不能去执行操作。

所以,将synchronized加在方法上,如果其保护的临界区代码段包含的临界区资源多于一个,就会造成临界区资源的闲置等待,进而会影响临界区代码段的吞吐量。为了提升吞吐量,可以将synchronized关键字放在函数体内,同步一个代码块。synchronized同步块的写法是:

synchronized (syncObject){
    // 临界区代码段的代码块
}

在synchronized同步块后边的括号中是一个syncObject对象,代表着进入临界区代码段需要获取syncObject对象的监视锁,由于每一个Java对象都有一把监视锁,因此任何Java对象都能作为synchronized的同步锁。

使用synchronized同步块对上面的TwoPlus类进行吞吐量的提升改造,具体的代码如下:

public class TwoPlus {
    private int num1 = 0;
    private int num2 = 0;
    // 两把不同的锁对象
    private Object object1 = new Object();
    private Object object2 = new Object();
    public  void plus(int val1,int val2){
        synchronized (object1){
            this.num1 = num1+val1;
        }
        synchronized (object2){
            this.num2 = num2+val2;
        }
    }
}

改造之后,对两个独立的临界区资源sum1和sum2的加法操作可以并发执行了,在某一个时刻,不同的线程可以对sum1和sum2同时进行加法操作,提升了plus()方法的吞吐量。

synchronized方法和synchronized同步块有什么区别呢?

总体来说,synchronized方法是一种粗粒度的并发控制,某一时刻只能有一个线程执行该synchronized方法;而synchronized代码块是一种细粒度的并发控制,处于synchronized块之外的其他代码是可以被多个线程并发访问的。在一个方法中,并不一定所有代码都是临界区代码段,可能只有几行代码会涉及线程同步问题。所以synchronized代码块比synchronized方法更加细粒度地控制了多个线程的同步访问。

synchronized方法的同步锁实质上使用了this对象锁,这样就免去了手工设置同步锁的工作。而使用synchronized代码块需要手工设置同步锁。

2. synchronized同步代码块

public class RoomDemo {
    private static int count = 0;
    // 创建锁对象,同步代码块需要手动设置对象锁
    private static Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for(int i=0;i<5000;i++){
                // 使用object对象锁住临界区资源
                synchronized (object){
                    count++;
                }
            }
        },"t1");
        Thread t2 = new Thread(()->{
            // 使用object对象锁住临界区资源
            for(int i=0;i<5000;i++){
                synchronized (object){
                    count--;
                }
            }
        },"t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);// 0
    }
}

在这里插入图片描述

你可以做这样的类比: synchronized(对象) 中的对象,可以想象为一个房间,线程 t1,t2 想象成两个人

(1) 当线程 t1 执行到 synchronized(object) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行 count++ 代码 ;

(2) 这时候如果 t2 也运行到了 synchronized(object) 时,它发现门被锁住了,只能在门外等待,发生了上下文切换,阻塞住了 ;

(3) 这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外 (不要错误理解为锁住了对象就能一直执行下去哦) , 这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时才 能开门进入

(4) 当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程并把钥匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码;

在这里插入图片描述

3. 如果同步块内的线程抛出异常会发生什么?

public class ExceptionDemo {
    private static int count = 1;
    // 创建锁对象
    private static Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            synchronized (object){
                System.out.println("线程t1正在执行");
                // 死循环
                while (count==1){
                }
            }
        },"t1");
        Thread t2 = new Thread(()->{
            synchronized (object) {
                System.out.println("线程t2正在执行");
                count--;
            }
        },"t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

执行结果:

在这里插入图片描述

可以看出线程t1执行的是死循环,所以每次线程上下文切换,线程t2都被阻塞了,拿不到锁,从而无法执行。

假如我们在线程执行过程中制造一个异常:

public class ExceptionDemo {
    private static int count = 1;
    // 创建锁对象
    private static Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            synchronized (object){
                System.out.println("线程t1正在执行");
                while (count==1){
                    // 死循环中制造异常
                    Integer.parseInt("a");
                }
            }
        },"t1");
        Thread t2 = new Thread(()->{
            synchronized (object) {
                System.out.println("线程t2正在执行");
                count--;
            }
        },"t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

执行结果:

在这里插入图片描述

当持有锁对象的线程在执行同步代码快中的代码时,如果出现异常,会释放锁,从而线程t2就可以拿到锁对象去执行自己同步代码块中的代码了。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!     

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java多线程之synchronized同步代码块详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java多线程之synchronized同步代码块的示例分析

小编给大家分享一下Java多线程之synchronized同步代码块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1. 同步方法和同步块,哪种更好?同步
2023-06-29

Java线程之线程同步synchronized和volatile详解

上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程共享的,一个线程改变了其成员变量num值
2023-05-30

Java多线程同步器代码详解

同步器为每种特定的同步问题提供了解决方案,同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier 和ExchangerSemaphoreSem
2023-05-30

Java 多线程同步 锁机制与synchronized深入解析

从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间
2022-11-15

Java中怎么利用Synchronized实现多线程同步

这期内容当中小编将会给大家带来有关Java中怎么利用Synchronized实现多线程同步,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。使用同步的原因1. 在系统中对访类要使用多线程进行访问;2. 在该类
2023-06-17

Java线程同步Lock同步锁代码示例

java线程同步原理java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronizedmethods)被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。当一个线程调用一个对象的同步
2023-05-30

Java学习之线程同步与线程间通信详解

这篇文章主要为大家详细介绍了线程同步和线程之间的通信的相关知识,文中的示例代码讲解详细,对我们学习Java有一定的帮助,感兴趣的可以了解一下
2022-12-27

java多线程编程同步器Future和FutureTask解析及代码示例

publicinterfaceFutureFuture表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用get方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由canc
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录