synchronized的性质
synchronized在前面的文章中被称为同步锁,其实它的名称还不只这个,它也叫互斥锁.但是这里为了方便理解就把它理解为厕所的门锁.
1.互斥
举个例子,有个人上厕所把门给锁上,门锁会从"无人"的绿色标识转换到"有人"的红色标识.在厕所外面的人想要使用厕所就得在外面排队.
这里就相当于一个类的对象使用了带有synchronized的方法,synchronized锁就锁上了.这个时候其他对象如果需要使用带synchronized的方法就得等待这个对象运行结束.
不过需要注意的是:
- 这里上一个线程解锁之后,下一个线程不是立刻直接获得锁,而是通过操作系统"唤醒",这也是操作系统调度的一部分.
- A先使用了synchronized方法,B接着使用,C再使用.这时候A先进入,B和C在外边排队.但是如果A使用结束,BC不是按照最开始的顺序进入排队,而是重新竞争.这里排队中的对象并不遵循先来后到的规则.
2.刷新内存
- 获得互斥锁(同步锁)
- 将主内存中的变量的副本拷贝到工作内存
- 执行代码
- 将更新过的变量的值同步到主内存
- 解除互斥锁(同步锁)
所以synchronized也能保证内存的可见性
3.可重入
顾名思义,就是可以重新进入自己使用的"厕所".
先解释一下synchronized的作用对象,它作用的对象是带有synchronized的方法的类的实例化(对象).
先说说什么是不可重入吧.不可重入就是一个人等完厕所后,上了厕所把门锁上了,然后自己闪现到厕所外并且失忆了,只记得在等厕所.这时候厕所是锁上的自己也进不去,
那么可重入就是自己上的厕所可以再进去…(怎么说起来怪怪的,诶呀只是方便理解,差不多那个意思).
一个对象拥有多个带有互斥锁(同步锁)的方法.其中方法一个方法二都是带有互斥锁的方法,方法二调用方法一.
static class Counter {
public int count = 0;
synchronized void increase() {
count++;
}
synchronized void increase2() {
increase();
}
}
public static void main(String[] args) {
Counter counter= new Counter();
counter.increase();
System.out.println(counter.count);
counter.increase2();
System.out.println(counter.count);
}
synchronized的使用
对方法使用
注意:
直接对方法添加
对普通方法使用
public synchronized void method_01{
System.out.println("01");
}
对静态方法使用
public synchronized static void method_02{
System.out.println("02");
}
对代码块使用
注意:
- 对代码块使用需要放在方法中
- 需要指定锁的对象
锁当前对象
public void method_03(){
synchronized (this){
System.out.println("03");
}
}
锁其他对象
public void method_04(){
synchronized (Counter.class){
System.out.println("04");
}
}
小知识点:
如果两个线程都竞争同一把锁,这才会产生阻塞.如果两个线程使用不同的两把锁,那么就不会竞争.
对代码块使用时()不能传入:
局部、构造、静态
- 局部变量:不能直接使用synchronized关键字修饰局部变量,因为局部变量没有类或对象的概念,无法作为锁对象。
- 构造函数:虽然理论上可以使用synchronized关键字修饰构造函数,但这在实际编程中很少见,因为构造函数通常用于初始化对象,而不是需要同步访问的操作。
- 静态代码块:静态代码块在类加载时执行,不能使用synchronized关键字,因为在类加载时还没有对象实例。
java标准库中的线程安全类
不安全类
涉及多线程修改共享数据,但是有没有使用互斥锁(同步锁)
两个List–ArrayList,LinkedList
两个Set–TreeSet,HashSet
两个Map–TreeMap,HashMap
一个–StringBuilder
安全类
使用了一些锁机制来控制
Vector(不推荐)
HashTable(不推荐)
ConcurrentHashMap
StringBuffer
StringBuffer的核心方法都涉及锁
还有一个不涉及"修改",不使用锁但是依然线程安全的(这是另外在常量池创立一个新的字符串,然后再指向这个新的字符串,所以线程安全)
String