Java 中的 synchronized 同步锁

news2024/10/5 21:25:07

        导致线程安全问题的根本原因在于,存在多个线程同时操作一个共享资源,要想解决这个问题,就需要保证对共享资源访问的独占性,因此人们在Java中提供了synchronized关键字,我们称之为同步锁,它可以保证在同一时刻,只允许一个线程执行某个方法或代码块。
        synchronized同步锁具有互斥性,这相当于线程由并行执行变成串行执行,保证了线程的安全性,但是损失了性能。下面我们先来看一下synchronized的使用方法。

synchronized 的使用方法

synchronized 的使用方法比较简单,修饰方式有如下两种。

  • 作用在方法级别,表示针对m1()方法加锁,当多个线程同时访问m1()方法时,同一时刻只有一个线程能执行。
public synchronized void m1(){
    //省略代码
}


作用在代码块级别,表示针对某一段线程不安全的代码加锁,只有访问到synchronized(this)这行代码时,才会去竞争锁资源。
 

public void m2( ){ 
    synchronized(this){
        //省略代码
    }
}

        了解了 synchronized的基本使用语法之后,我们来看如图所示的流程,它针对上面的案例增加了 synchronized 同步锁之后的执行流程。简单地说,当多个线程同时访问加synchronized关键字修饰的方法时,需要先抢占一个锁标记,只有抢到锁标记的线程才有资格调用incr()方法。这就使得在同一时刻只有一个线程执行i++操作,从而解决了原子性问题。

了解 synchronized 同步锁的作用范围

        我们对一个方法增加synchronized关键字后,当多个线程访问该方法时,整个执行过程会变成串行执行,这种执行方式很明显会影响程序的性能,那么如何做好安全性及性能的平衡呢?
        实际上,synchronized关键字只需要保护可能存在线程安全问题的代码,因此,我们可以通过控制同步锁的作用范围来实现这个平衡机制。在synchronized 中,提供了两种锁,一是类锁,二是对象锁。
        类锁
        类锁是全局锁,当多个线程调用不同对象实例的同步方法时会产生互斥,具体实现方式如下。  

  • 修饰静态方法:
public static synchronized void m1( ){
    //省略代码
}
  • 修饰代码块,synchronized 中的锁对象是类,也就是Lock.class。
public class Lock{ 
    public void m2(){
        synchronized(Lock.class){
            //省略代码
        }
    }
}


        下面这段程序使用类锁来实现跨对象实例,从而实现互斥的功能。

public class SynchronizedExample{

    public void m1( ) {
        synchronized(SynchronizedExample.class) {
            while (true){
                System.out.println("当前访问的线程:"+Thread.currentThread( ).getName());                         
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args){
        SynchronizedExample se1=new SynchronizedExample(); 
        SynchronizedExample se2=new SynchronizedExample(); 
        new Thread(()->se1.m1(),"t1").start(); 
        new Thread(()->se2.m1(),"t2" ).start();
    }
}
  • 该程序中定义了一个m1()方法,该方法中实现了一个循环打印当前线程名称的逻辑,并且这段逻辑是用类锁来保护的。
  • 在 main()方法中定义了两个SynchronizedExample对象实例sel和se2,又分别定义了两个线程来调用这两个实例的m10方法。

        根据类锁的作用范围可以知道,即便是多个对象实例,也能够达到互斥的目的,因此最终输出的结果是:哪个线程抢到了锁,哪个线程就持续打印自己的线程名称。

        对象锁
        对象锁是实例锁,当多个线程调用同一个对象实例的同步方法时会产生互斥,具体实现方式如下。

  • 修饰普通方法:
public synchronized void m1( ){
    //省略代码
}
  • 修饰代码块,synchronized中的锁对象是普通对象实例。
public class Lock{
    Object lock=new 0bject( ); public void m2( ){
        synchronized(lock){
            //省略代码
        }
    }
}

        下面这段程序演示了对象锁的使用方法,代码如下。

public class SynchronizedForobjectExample {
    Object lock=new 0bject(); 
    public void m1( ){
        synchronized (lock){
            while(true){
                System.out.println("当前获得锁的线程:"+Thread.currentThread().getName());
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();}
                }
        }
    }

    public static void main(String[] args) {
        SynchronizedFor0bjectExample se1=new SynchronizedForobjectExample();             
        SynchronizedForObjectExample se2=new SynchronizedForobjectExample(); 
        new Thread(()->se1.m1(),"t1").start(); 
        new Thread(()->se2.m1(),"t2" ).start();
    }
}

我们先来看一下打印结果。

当前获得锁的线程:t1

当前获得锁的线程:t2

当前获得锁的线程:t2

当前获得锁的线程:t1

当前获得锁的线程:t1

当前获得锁的线程:t2

当前获得锁的线程:t1

当前获得锁的线程:t2


        从以上结果中我们发现,对于几乎相同的代码,在使用对象锁的情况下,当两个线程分别访问两个不同对象实例的m10方法时,并没有达到两者互斥的目的,看起来似乎锁没有生效,实际上并不是锁没有生效,问题的根源在于synchronized(lock)中锁对象lock的作用范围过小。
        Class是在JVM启动过程中加载的,每个.class文件被装载后会产生一个Class对象,Class对象在JVM进程中是全局唯一的。通过static修饰的成员对象及方法的生命周期都属于类级别,它们会随着类的定义被分配和装载到内存,随着类被卸载而回收。
        实例对象的生命周期伴随着实例对象的创建而开始,同时伴随着实例对象的回收而结束。

        因此,类锁和对象锁最大的区别是锁对象lock的生命周期不同,如果要达到多个线程互斥,那么多个线程必须要竞争同一个对象锁。
        在上述代码中,通过Objectlock-new Object();构建的锁对象的生命周期是由Synchronized- ForObjectExample 对象的实例来决定的,不同的SynchronizedForObjectExample 实例会有不同的 lock锁对象,由于没有形成竞争,所以不会实现互斥的效果。如果想要让上述程序达到同步的目的,那么我们可以对lock锁对象增加.static关键字。

static Object lock=new 0bject();

最后,留下一个问题去思考,关于 synchronized 同步锁的思考?同步锁的核心特性是排他,要达到这个目的,多线程必须抢占同一个资源。。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣每日一题83:删除排序数组中的重复元素

题目描述: 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 示例 1: 输入:head [1,1,2] 输出:[1,2]示例 2: 输入:head [1,1,2…

Day14力扣打卡

打卡记录 H 指数(二分) 链接 以最大值 x 为分割点的正整数数轴上,满足: 少于等于 x 的数值必然满足条件;大于 x 的数值必然不满足。 采用右边界二分查找,寻找满足条件的最大 H 指数要求。 class Soluti…

shell脚本函数(极其粗糙版)

分界点:以下内容需要更改,正常放假更改 函数: 1、把整个命令序列按照格式写在一起 2、可以方便的重复使用的命令序列 使用函数可以避免代码重复 函数可以将大的工程分割为诺干小的功能模块,可以随时调用,代码的可读…

Windows一键添加命名前缀(文件)

温馨提示:使用前建议先进行测试和原文件备份,避免引起不必要的损失。 (一)需求描述 在上班摸鱼的我正准备打开手机刷会儿CSDN论坛,老板发给我一个压缩包并要求我给里面所有的文件的名称添加一个前缀”大项目_”。我本…

协同过滤音乐推荐系统

摘 要 信息化爆炸的时代,互联网技术的指数型的增长,信息化程度的不断普及,社会节奏在加快,每天都有大量的信息扑面而来,人们正处于数字信息化世界。数字化的互联网具有便捷性,传递快,效率高&am…

windows系统命令常用命令(常看当前路径等)

打开cmd命令行终端 按下 winr 快捷键,输入cmd,点击确定 查看当前路径 输入命令 chdir 查看目录下文件 输入命令 dir //查看当前目录下的文件,类似于linux下的ls cd /d e:\software //跳转到其他硬盘的其他文件夹,注意此处…

Linux权限的概念

文章目录 Linux权限管理01.文件访问者的分类(人)02.文件类型和访问权限(事物属性)03.文件权限值的表示方法04. 调整权限(文件访问权限的相关设置方法) ch系列是改变文件性质的指令;file指令可以…

CS224W2.3——传统基于特征的方法(图层级特征)

前两篇中我们讨论了节点层级的特征表示、边层级的特征表示: CS224W2.1——传统基于特征的方法(节点层级特征)CS224W2.2——传统基于特征的方法(边层级特征) 在这篇中,我们将重点从整个图中提取特征。换句话说,我们想要描述整个图结构的特征…

2.27每日一题(定积分求面积,旋转体体积)

1、遇到求面积、定积分的问题,先画图: (1)抛物线(可正可负,所以抛物线函数需要有 a 变量) (2)过两个点 2、定积分求面积有三种情况对应三条公式: &#xff0…

H5游戏源码分享-像素小鸟游戏(类似深海潜艇)

H5游戏源码分享-像素小鸟游戏&#xff08;类似深海潜艇&#xff09; 点击屏幕控制小鸟的飞行高度 整个小游戏就用JS完成 项目地址&#xff1a;https://download.csdn.net/download/Highning0007/88483228 <!DOCTYPE HTML> <html><head><meta http-equiv…

【C++】智能指针总结:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)

文章目录 0. 概述智能指针&#xff0c;智能在哪儿&#xff1f;RAII 的介绍四个智能指针的特点&#xff1a; 1. auto_ptr&#xff08;C98&#xff09;&#x1f40e;核心功能的简单实现 2. unique_ptr&#xff08;C11&#xff09;&#x1f40e;核心功能的简单实现 3. shared_ptr&…

LSMW应用

案 执行第二行 第六步直接跳过.

【2023MathorCup大数据竞赛】B题完整解答过程(思路+模型文档+代码+结果)

B题完整解答过程 写在最后技术文档&#xff08;部分&#xff09;问题分析假设符号说明1 基于自适应ARIMA-LR模型的需求量预测&#xff08;问题一第一小问&#xff09;1.1 ARIMA模型的建立1.2 LR模型的建立1.3 自适应混合ARIMA-LR模型的建立1.4 ARIMA模型的求解1.5 LR模型的求解…

图像分类任务ViT与CNN谁更胜一筹?DeepMind用实验证明

精华置顶 墙裂推荐&#xff01;小白如何1个月系统学习CV核心知识&#xff1a;链接 点击CV计算机视觉&#xff0c;关注更多CV干货 今天跟大家分享DeepMind发表的一篇技术报告&#xff0c;通过实验得出&#xff0c;CNN与ViT的架构之间虽然存在差异&#xff0c;但同等计算资源的预…

力扣刷题 day59:10-29

1.子集 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 方法一&#xff1a;二进制枚举 #方法一&#xff1a;二进制枚举 def subsets(nums)…

C# “依赖注入” 中的 “三种生命周期”

&#x1f680;简介 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种实现控制反转&#xff08;IoC&#xff09;的技术&#xff0c;用于减少代码之间的耦合度。通过依赖注入&#xff0c;一个类可以从外部获取其依赖的对象&#xff0c;而不是自己…

MyBatis-Plus 与 Druid 结合 Dynamic-datasource 实现多数据源操作数据库

MyBatis-Plus 官网&#xff1a;https://baomidou.com/ MyBatis-Plus 官方文档&#xff1a;https://baomidou.com/pages/24112f/ dynamic-datasource 文档&#xff08;付费&#xff09;&#xff1a;https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611 创建数据库…

EM算法解析+代码

大纲 数学基础&#xff1a;凸凹函数&#xff0c;Jensen不等式&#xff0c;MLEEM算法公式&#xff0c;收敛性HMM高斯混合模型 一、数学基础 1. 凸函数 通常在实际中&#xff0c;最小化的函数有几个极值&#xff0c;所以最优化算法得出的极值不确实是否为全局的极值&#xff…

腾讯云2023年双11服务器优惠活动及价格表

腾讯云2023年双11大促活动正在火热进行中&#xff0c;腾讯云推出了一系列服务器优惠活动&#xff0c;云服务器首年1.8折起&#xff0c;买1年送3个月&#xff01;境外云服务器15元/月起&#xff0c;买更多省更多&#xff01;下面给大家分享腾讯云双11服务器优惠活动及价格表&…

Ps:平滑选区边缘

Ps 中某些传统的选区工具&#xff0c;如快速选择工具等&#xff0c;在进行选区后容易产生锯齿状等不规则的边界。此时&#xff0c;可利用“选择并遮住”来平滑选区边缘。 ◆ ◆ ◆ 操作方法与技巧 1、&#xff08;可选&#xff09;新建纯色填充图层&#xff0c;并置于原图图层…