synchronized如何保证三大特性
保证在同一时刻最多只有一个线程执行,以达到保证并发安全的效果
synchronized(锁对象){
//受保护的资源
}
synchronized保护原子性的原理
synchronized保证原子性的原理,synchronized保证只有一个线程拿到锁,能够进入同步代码块;
synchronized解决可见性
执行synchronized时,会对应lock原子操作会刷新工作内存中共享变量的值
synchronized解决重排序
package com.synconized;
import java.util.HashSet;
public class Demo4 {
static int x;
static int y;
static int a;
static int b;
static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
HashSet<String> result= new HashSet<>();
while (true){
x =0;
y =0;
a =0;
b =0;
//线程1
Thread t1 = new Thread(()->{
synchronized (obj) {
a = y; // a = 0
x = 1;
}
});
//线程2
Thread t2 = new Thread(()->{
synchronized (obj) {
b = x; //b = 0
y = 1;
}
});
t1.start();
t2.start();
t2.join();
t1.join();
result.add("[a="+a + ",b=" + b+"]");
System.out.println(result);
/*
多跑一会结果出现的情况:
[a=0,b=0, a=1,b=0, a=0,b=1, a=1,b=1]
先运行t1线程结果就是:a=0,b=1
先运行t2线程结果就是:a=1,b=0
其他两种结果是顺序重排了
*/
}
}
}
锁的原理,同一时间只允许同步块中有一个线程执行
synchronized的可重入特性
1 什么是可重入(递归锁):
指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。类车牌照摇号,可重入就是拿到之后就可以再次用起来,可以一直摇号,如果还需要重新竞争的话就叫不可重入锁
synchronized的锁对象中有一个计数器(recursions变量)会记录线程获得几次锁,在执行完同步代码块时,计数器的数量会-1,直到计数器的数量为0,就释放这个锁
2 可重入有什么好处:
避免死锁:如果不可重入就会一直等待锁,很容易造成死锁
提升封装性:同步代码块可以调用另一个同步代码块,方便通过方法封装
package com.synconized;
import java.util.concurrent.TimeUnit;
public class Demo5 {
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
}
}
class MyThread extends Thread{
public MyThread() {
System.out.println(getName()+"启动了");
}
@Override
public void run() {
synchronized (MyThread.class){
//获取到锁,计数器+1
System.out.println(getName()+"获取到锁,进入同步代码块1开始运行");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyThread.class){
// 再次获取到锁,计数器+1
System.out.println(getName()+"获取到锁,进入同步代码块2开始运行");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
一个线程获取到锁则执行逻辑,另外一个获取不到锁的线程就会进入等待,直到前面线程把锁释放
如果锁一直没有释放,那么其他需要锁的线程将无法执行,线程死锁。