【JavaEE初阶】 synchronized关键字详解

news2024/11/19 2:22:34

文章目录

  • 🌴synchronized 的特性
    • 🚩互斥
    • 🚩可重入
  • 🍀synchronized 的使用
    • 🚩直接修饰普通方法
    • 🚩修饰静态方法
    • 🚩 修饰代码块
    • 🧭synchronized 的使用总结:
  • 🎍Java 标准库中的线程安全类
  • ⭕总结

本文重点:

  • synchronized 的特性
  • synchronized 使用示例
  • ava 标准库中的线程安全类

🌴synchronized 的特性

🚩互斥

synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到
同一个对象 synchronized 就会阻塞等待.

  • 进入 synchronized 修饰的代码块, 相当于 加锁

  • 退出 synchronized 修饰的代码块, 相当于 解锁
    在这里插入图片描述
    synchronized用的锁是存在Java对象头里的。

在这里插入图片描述
可以粗略理解成, 每个对象在内存中存储的时候, 都存有一块内存表示当前的 “锁定” 状态(类似于厕所的 “有人/无人”).

  • 如果当前是 “无人” 状态, 那么就可以使用, 使用时需要设为 “有人” 状态.

  • 如果当前是 “有人” 状态, 那么其他人无法使用, 只能排队

在这里插入图片描述

后面排队的人我们可以称为“阻塞等待”

阻塞等待:针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的线程, 再来获取到这个锁.

注意:

  • 上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 “唤醒”. 这也就是操作系统线程调度的一部分工作.
  • 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B和 C 都在阻塞队列中排队等待.但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则

补充:synchronized的底层是使用操作系统的mutex lock实现的

🚩可重入

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题

那么什么是可重入?怎么自己把自己锁死呢?

理解 “把自己锁死”:

一个线程没有释放锁, 然后又尝试再次加锁

  • 按照之前对于锁的设定, 第二次加锁的时候, 就会阻塞等待.

  • 直到第一次的锁被释放, 才能获取到第二个锁.

  • 但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作.

  • 这时候就会死锁

举个例子:

一个滑稽老铁去上厕所,反锁厕所们后,然后不小心一个闪现出来了,还失忆了,这时候厕所没人,但是处于锁的状态,后面等待的人无法进入
在这里插入图片描述
这样的锁称为不可重入锁

Java 中的== synchronized 是可重入锁==, 因此没有上面的问题

代码示例

public class Counter {
	public int count = 0;
	synchronized void increase() {
		count++;
	}
	synchronized void increase2() {
		increase();
	}
}

在上面的代码中,

  • increase 和 increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对== this 当前对象==加锁的.

  • 在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释放, 相当于连续加两次锁)

  • 这个代码是完全没问题的.

因为 synchronized 是可重入锁

在可重入锁的内部, 包含了 “线程持有者” 和 “计数器” 两个信息.

  • 如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取到锁, 并让计数器自增.

  • 解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到)

🍀synchronized 的使用

synchronized 本质上要修改指定对象的 “对象头”. 从使用角度来看, synchronized 也势必要搭配一个具
体的对象来使用

synchronized的使用方法可以分为以下三种:

🚩直接修饰普通方法

锁是当前实例对象 ,进入同步代码前要获得当前实例的锁

锁的 SynchronizedDemo对象

public class SynchronizedDemo {
	public synchronized void methond() {
	}
}

使用举例:

多个线程访问同一个对象的同一个方法

public class synchronizedTest implements Runnable {
    //共享资源
    static int i =0;
    /**
     * synchronized 修饰实例方法
     */
    public synchronized void increase(){
        i++;
    }
    @Override
    public void run(){
        for (int j =0 ; j<10000;j++){
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        synchronizedTest test = new synchronizedTest();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

运行结果如下:
在这里插入图片描述
分析:当两个线程同时对一个对象的一个方法进行操作,只有一个线程能够抢到锁。因为一个对象只有一把锁,一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,就不能访问该对象的其他synchronized实例方法,需要等到对象被释放后才能获取,但是在对象没有被释放前,其他线程可以访问非synchronized修饰的方法

🚩修饰静态方法

锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁

锁的 SynchronizedDemo 类的对象

public class SynchronizedDemo {
	public synchronized static void method() {
	
		}
}

使用举例

public class synchronizedTest3 implements Runnable {
    //共享资源
    static int i = 0;

    /**
     * synchronized 修饰静态方法
     */
    public static synchronized void increase() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            increase();
        }
    }
}


public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new synchronizedTest3());
        Thread t2 = new Thread(new synchronizedTest3());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(synchronizedTest3.i);
    }
}

结果如下:
在这里插入图片描述
分析:由例子可知,两个线程实例化两个不同的对象,但是访问的方法是静态的,两个线程发生了互斥(即一个线程访问,另一个线程只能等着),因为静态方法是依附于类而不是对象的,当synchronized修饰静态方法时,锁是class对象

🚩 修饰代码块

同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

  • 锁当前对象
public class SynchronizedDemo {
	public void method() {
		synchronized (this) {
		
		}
	}
}
  • 锁类对象
public class SynchronizedDemo {
	public void method() {
		synchronized (SynchronizedDemo.class) {
		
		}
	}
}

🧭synchronized 的使用总结:

我们重点要理解,synchronized 锁的是什么. 两个线程竞争同一把锁, 才会产生阻塞等待

两个线程分别尝试获取两把不同的锁, 不会产生竞争.
在这里插入图片描述

🎍Java 标准库中的线程安全类

Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, 又没有任何加锁措施.

  • ArrayList

  • LinkedList

  • HashMap

  • TreeMap

  • HashSet

  • TreeSet

  • StringBuilde

但是还有一些是线程安全的. 使用了一些锁机制来控制

  • Vector (不推荐使用)

  • HashTable (不推荐使用)

  • ConcurrentHashMap

  • StringBuffer

不推荐使用的原因是:当你使用这些类时,每一次都会伴随加锁操作,如果该操作不需要加锁,则就会造成时间浪费。

注意String还有的虽然没有加锁, 但是不涉及 “修改”, 仍然是线程安全的

⭕总结

关于《【JavaEE初阶】 synchronized关键字详解》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

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

相关文章

TCP/IP(十一)TCP的连接管理(八)socket网络编程

一 socket网络编程 socket 基本操作函数 bind、listen、connect、accept、recv、send、select、close 说明: 本文需要C语言、syscall系统调用、OS 操作系统支持,如果不了解可以暂时跳过备注&#xff1a; 知道对应库函数的更底层机制思考&#xff1a; socket函数与FIN、ACK等…

Malformed \uxxxx encoding.问题解决方案

问题背景 Maven项目构建时报错如下&#xff0c; [ERROR] Malformed \uxxxx encoding. [ERROR] java.lang.IllegalArgumentException: Malformed \uxxxx encoding. [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re…

测试岗最好用的——十大软件测试工具

前言 目前由于软件测试工作在软件的生产过程中越来越重要&#xff0c;很多软件测试工具应运而生&#xff0c;这里介绍一下目前最流行的一些软件测试工具&#xff0c;一个十个&#xff0c;介绍如下&#xff1a; 一、企业级自动化测试工具WinRunner 这款软件是Mercury Interact…

[NPUCTF2020]ezinclude 文件包含两大 getshell方式

PHP LFI 利用临时文件 Getshell 姿势 | 码农家园 说一下我的思路吧 robots没有 扫描发现存在 dir.php 然后404.html 报错 apache 2.18 ubuntu 这个又正好存在漏洞 所以前面全去看这个了 结果根本不是这样做。。。 正确的思路是这样 发现变量 认为是 name和 pass传递参数…

Python 机器学习入门之牛顿法

系列文章目录 第一章 Python 机器学习入门之线性回归 第一章 Python 机器学习入门之梯度下降法 第一章 Python 机器学习入门之牛顿法 梯度下降法 系列文章目录前言一、牛顿法1.牛顿法简介2.基本原理 总结 前言 上一篇文章里面说到了梯度下降法&#xff0c;它是使用泰勒近似定…

第18篇ESP32platformio-arduino框架-ili9488-3.5lcd显示时间天气

第18篇ESP32platformio-arduino框架-ili9488-lcd显示时间天气 第18篇esp32ili9488lcd显示时间天气 连接方法&#xff1a; 修改WIFI&#xff1a; 关键代码 void setup() {Serial.begin(115200);WiFi.mode(WIFI_STA);WiFi.begin(ssid,password);Serial.print("\r\nConnect…

codesys【软PLC】

软plc一般安装在工控机里。或者笔记本等小型电脑内。 电视盒子需要&#xff1a;刷个armbain 软件下载地址&#xff1a; CODESYS商城-商品列表 A&#xff1a;是实时系统 B&#xff1a;非实时 C&#xff1a;这个不能单独运行&#xff0c;需要在B的基础上。开启了多核线程…

国际教材概念基础

各种区别 缩写 A-LEVEL&#xff08;大学预科&#xff09;&#xff1a;General Certificate of Education Advanced Level AP&#xff1a;Advanced Placement&#xff08;美国地区&#xff1a;美高AP&#xff09; GCSE&#xff1a;General Certificate of Secondary Educati…

下载调试器 JTAG和SWD

下载调试器 JTAG和SWD 作为嵌入式工程师&#xff0c;下载调试器 JTAG和SWD是经常使用的&#xff0c;但你真正了解其SWD 和 JTAG接口的含义和区别吗&#xff1f; 一、什么是下载调试器&#xff1f; 下载调试器是将PC&#xff08;例如通过USB协议&#xff09;发送的命令转换为…

数组的reduce和reduceRight方法

1.reduce方法可用接收两个参数&#xff1a; 第一个参数&#xff1a;为一个回调函数&#xff0c;此回调函数又可以有四个参数 第1个参数&#xff1a;为上一次回调函数return的结果&#xff0c;首次默认为第二个参数值&#xff0c;如果没有第二个参数值&#xff0c;则默认当前数…

[Mono Depth/3DOD]单目3D检测基础

1.数据增强 图像放缩和裁剪后&#xff0c;相机内参要做相应变化 import random def random_scale(image, calib, scale_range(0.8, 1.2)):scale random.uniform(*scale_range)width, height image.sizeimage image.resize((int(width * scale), int(height * scale)))cali…

4.03 用户中心-订单管理功能开发

用户中心-订单管理功能开发: (1&#xff09;包含用户订单的查询根据订单并进行分页 (2&#xff09;订单的确定和交易关闭订单删除 详细内容&#xff0c;查询地址&#xff1a;http://www.gxcode.top/code 页面下载地址&#xff1a;

【C++】神奇字符串(力扣481)

神奇字符串的规律&#xff1a; 神奇字符串 s 仅由 ‘1’ 和 ‘2’ 组成&#xff0c;并需要遵守下面的规则&#xff1a; 神奇字符串 s 的神奇之处在于&#xff0c;串联字符串中 1 和 2 的连续出现次数可以生成该字符串。 s 的前几个元素是 s “1221121221221121122……” 。如果…

项目管理过程组

项目管理有2条主线&#xff0c;一条是技术&#xff0c;一条是管理。项目过程由项目团队实施。一般术语以下两大类之一&#xff1a;一类是项目管理过程。另一类是面向产品的过程。在大多数情况下&#xff0c;大多数项目都有共同的项目管理过程。它们通过有目的的实施而互相联系起…

界面组件DevExtreme v23.2新功能预览 - 增强Material主题功能

界面组件DevExtreme v23.2即将在几个月之后交付&#xff0c;本文将带大家预览一波新功能。 DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuer…

IOday3作业

#include <head.h> int get_filePerrmison(mode_t mode)//获取文件权限 {char per[] "rwx";for(int i0;i<9;i){if((mode&(0400>>i))0){putchar(-);continue;}putchar(per[i%3]);}} int get_fileType(mode_t m) //获取文件类型 {switch(m&S_IF…

蓝桥杯每日一题2023.10.11

子串分值和 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 以下为50分方法&#xff08;暴力枚举&#xff09; 第一层循环枚举其长度&#xff0c;第二层循环枚举其左端点&#xff0c;k代表右端点&#xff0c;&#xff08;将每一种子串一一枚举出来&#xff09;算出从左端点到右…

关于Vuex的基础使用存值及异步

目录 一.概述 二.取值 2.1.安装 2.2.菜单栏 2.3.模块 2.4.引用 三.改值 四.异步&后台请求 好啦今天就到这里了希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.概述 Vuex 是一个用于 Vue.js 应用程序的状态管理库。它主要用于集中管理应用程序中的共享状态&a…

kafka详解(三)

2.2 Kafka命令行操作 2.2.1 主题命令行操作 1&#xff09;查看操作主题命令参数 [aahadoop102 kafka]$ bin/kafka-topics.sh2&#xff09;查看当前服务器中的所有topic (配置了环境变量不需要写bin/) [aahadoop102 kafka]$ bin/kafka-topics.sh --bootstrap-server hadoop10…

vue3组件的通信方式

一、vue3组件通信方式 通信仓库地址:vue3_communication: 当前仓库为贾成豪老师使用组件通信案例 不管是vue2还是vue3,组件通信方式很重要,不管是项目还是面试都是经常用到的知识点。 比如:vue2组件通信方式 props:可以实现父子组件、子父组件、甚至兄弟组件通信 自定义事件:可…