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

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

synchronized 几个要点: 1)进入{就是加锁,进入}就是解锁 2)加锁 操作是会防止其他线程“插队”不影响本线程调度出 cpu(和临界区做区分) 3)锁对象,两个线程针对同一个对象加锁,才会有锁竞争,不同对象,则不会有~~
举一个例子,帮助理解
可以粗略理解成,每个对象在内存中存储的时候,都存有⼀块内存表示当前的"锁定"状态(类似于厕所 的"有人/无人"). 如果当前是"无人"状态,那么就可以使用,使用时需要设为"有人"状态. 如果当前是"有人"状态,那么其他人无法使用,只能排队

理解“阻塞等待”: 针对每一把锁,操作系统内部都维护了一个等待队列,当这个锁被某个线程占有的时候,其他线程尝试进行加锁,就加不上了,就会阻塞等待,一直等到之前的线程解锁之后,由操作系统唤醒一个新的线程,再来获取到这个锁 #注: 上一个线程解锁之后,下一个线程并不是立即就能获取到锁,而是要靠操作系统来“唤醒”,这也就是操作系统线程调度的一部分工作 假设有ABC三个线程,线程A先获取到锁,然后B尝试获取锁,然后C再尝试获取锁,此时B和C都在阻塞队列中排队等待,但是当A释放锁之后,虽然B比C先来的,但是B不一定就能获取到锁,而是和C重新竞争,并不遵守先来后到的规则
注意: synchronized的底层是使用操作系统的mutexlock实现的
同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题;
理解"把自己锁死" 一个线程没有释放锁,然后又尝试再次加锁. // 第一次加锁,加锁成功 lock(); // 第⼆次加锁,锁已经被占用,阻塞等待. lock(); 按照之前对于锁的设定,第⼆次加锁的时候,就会阻塞等待。直到第一次的锁被释放,才能获取到第⼆个锁,但是释放第一个锁也是由该线程来完成,结果这个线程已经躺平了,啥都不想干了,也就无法进行解锁操作,这时候就会死锁

这样的锁称为不可重入锁.
Java 中的synchronized是可重入锁,因此没有上面的问题
for (int i = 0; i < 50000; i++) {
synchronized (locker) {
synchronized (locker) {
count++;
}
}
}在可重入锁的内部,包含了"线程持有者"和"计数器"两个信息:
synchronized 本质上要修改指定对象的“对象头”,从使用角度来看,synchronized也势必要搭配一个具体的对象来使用
锁任意对象
public class SynchronizedDemo {
private Object locker = new Object();
public void method() {
synchronized (locker) {
}
}
}锁当前对象
public class SynchronizedDemo {
public void method() {
synchronized (this) {
}
}
} public class SynchronizedDemo {
public synchronized void methond() {
}
} public class SynchronizedDemo {
public synchronized static void method() {
}
}我们重点要理解,synchronized锁的是什么,两个线程竞争同一把锁,才会产生阻塞等待
两个线程分别尝试获取两把不同的锁,不会产生竞争

Java 标准库中很多都是线程不安全的.这些类可能会涉及到多线程修改共享数据,又没有任何加锁措 施.
• ArrayList
• LinkedList
• HashMap
• TreeMap
• HashSet
• TreeSet
• StringBuilder
但是还有⼀些是线程安全的.使用了⼀些锁机制来控制.
• Vector(不推荐使用)
• HashTable(不推荐使用)
• ConcurrentHashMap
• StringBuffer

StringBuffer 的核心方法都带有 synchronized
还有的虽然没有加锁,但是不涉及"修改",仍然是线程安全的
• String