Java EE初阶启程记06---synchronized关键字

🔥个人主页: 寻星探路

🎬作者简介:Java研发方向学习者

📖个人专栏:、《

⭐️人生格言:没有人生来就会编程,但我生来倔强!!!



目录

[一、synchronized关键字---监视器锁 monitor lock](#一、synchronized关键字---监视器锁 monitor lock)

1、synchronized的特性

1.1互斥锁

1.2可重入锁

[2、synchronized 使用示例](#2、synchronized 使用示例)

2.1修饰代码块:明确指定锁哪个对象

2.2直接修饰普通方法:锁的SynchronizedDemo对象

2.3修饰静态方法:锁的SynchronizedDemo类的对象

3、Java标准库中的线程安全类


一、synchronized关键字---监视器锁 monitor lock

1、synchronized的特性

1.1互斥锁

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

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

退出synchronized修饰的代码块,相当于解锁

synchronized用的锁是存在Java对象头里的

synchronized 几个要点:

1)进入{就是加锁,进入}就是解锁

2)加锁 操作是会防止其他线程"插队"不影响本线程调度出 cpu(和临界区做区分)

3)锁对象,两个线程针对同一个对象加锁,才会有锁竞争,不同对象,则不会有~~

可以粗略理解成,每个对象在内存中存储的时候,都存有一块内存表示当前的"锁定"状态(类似于厕所的"有人/无人")

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

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

(以搞对象为例子(当笑话就好,纯属虚构,只为了帮助理解!!!))

理解"阻塞等待":

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

#注:

上一个线程解锁之后,下一个线程并不是立即就能获取到锁,而是要靠操作系统来"唤醒",这也就是操作系统线程调度的一部分工作

假设有ABC三个线程,线程A先获取到锁,然后B尝试获取锁,然后C再尝试获取锁,此时B和C都在阻塞队列中排队等待,但是当A释放锁之后,虽然B比C先来的,但是B不一定就能获取到锁,而是和C重新竞争,并不遵守先来后到的规则

synchronized的底层是使用操作系统的mutexlock实现的

1.2可重入锁

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

理解"把自己锁死"

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

// 第一次加锁,加锁成功

lock();

// 第⼆次加锁,锁已经被占用,阻塞等待.

lock();

按照之前对于锁的设定,第⼆次加锁的时候,就会阻塞等待。直到第一次的锁被释放,才能获取到第⼆个锁,但是释放第一个锁也是由该线程来完成,结果这个线程已经躺平了,啥都不想干了,也就无法进行解锁操作,这时候就会死锁

这样的锁称为不可重入锁

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

java 复制代码
 for (int i = 0; i < 50000; i++) {
     synchronized (locker) {
         synchronized (locker) {
             count++;
         }
     }
 }

在可重⼊锁的内部,包含了"线程持有者"和"计数器"两个信息:

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

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

2、synchronized 使用示例

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

2.1修饰代码块:明确指定锁哪个对象

锁任意对象

java 复制代码
 public class SynchronizedDemo {
     private Object locker = new Object();

     public void method() {
         synchronized (locker) {

         }
     }
 }

锁当前对象

java 复制代码
 public class SynchronizedDemo {
     public void method() {
         synchronized (this) {

         }
     }
 }

2.2直接修饰普通方法:锁的SynchronizedDemo对象

java 复制代码
 public class SynchronizedDemo {
     public synchronized void methond() {
     }
 }

2.3修饰静态方法:锁的SynchronizedDemo类的对象

java 复制代码
 public class SynchronizedDemo {
     public synchronized static void method() {
     }
 }

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

两个线程分别尝试获取两把不同的锁,不会产生竞争

3、Java标准库中的线程安全类

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

ArrayList

LinkedList

HashMap

TreeMap

HashSet

TreeSet

StringBuilder

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

Vector(不推荐使用)

HashTable(不推荐使用)

ConcurrentHashMap

StringBuffer

StringBuffer 的核心方法都带有 synchronized

还有的虽然没有加锁,但是不涉及"修改",仍然是线程安全的

String

相关推荐
沉木渡香2 小时前
【VSCode中Java开发环境配置的三个层级之Maven篇】(Windows版)
java·vscode·maven
EnCi Zheng3 小时前
Spring Boot 4.0.0-SNAPSHOT @Configuration 问题解决指南
java·spring boot·spring
海上生明月丿4 小时前
在IDEA中使用Git
java·git·intellij-idea
托比-马奎尔4 小时前
Redis7内存数据库
java·redis·后端
鹅是开哥4 小时前
Redis的零食盒满了怎么办?详解缓存淘汰策略
java·redis·缓存·bootstrap
惜.己4 小时前
jmeter中java.net.ConnectException: Connection refused: connect
java·jmeter·.net
yunmi_4 小时前
分布式文件存储系统FastDFS(入门)
java·分布式·maven·fastdfs
K_i1346 小时前
指针步长:C/C++内存操控的核心法则
java·开发语言
宇宙超粒终端控制中心6 小时前
Java使用easypoi填充数据到word
java·spring boot·spring cloud·java-ee·easypoi