The purpose of life is to grow, the essence of life is to change, the challenge of life is to conquer
生活的目的是成长,生活的本质是变化,生活的挑战是征服
synchronized关键字几乎是每次面试的必考题,今天就来聊聊synchronized的用法以及它的使用场景
众所周知,Java 内存模型中有三大特性,原子性,可见性,有序性
synchronized:保证可见性和原子性
在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存-->在主内存中拷贝最新变量的副本到工作内存-->执行完代码-->将更改后的共享变量的值刷新到主内存中-->释放互斥锁
有序性:java程序中天然的有序性可以总结为一句话,如果在本线程内观察,所有操作都是天然有序的,如果在一个线程内观察另一个线程,所有操作都是无序的
且看下面采用双重校验锁单例模式
private volatile static Singleton singleton;
public static Singleton getSingleton(){
if(null == singleton){
synchronized (Singleton.class) {
if(null == singleton){
singleton = new Singleton();
}
}
}
return singleton;
}
public static void main(String[] args) {
for(int i = 0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+Singleton.getSingleton().hashCode());
}}).start();
}复制代码
大家可以看到,对象Singleton采用了volatile修饰,又进行了两次判空,采用volatile修饰实现了变量的修改可见性(volatile也是一个关键字),两次判空是因为第一次判空是单例模式只需要创建一次实例,如果后面再次调用,则直接返回之前创建的实例, 因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。第二次判空是假设线程1执行第一次校验后,此时线程2也获取了cpu执行权,接下来线程2获得锁,创建实例 这时线程1,获得执行权,也创建实例,结果就会导致创建多个实例,所以需要在同步代码里面进行第二次校验, 如果实例为空,则再进行创建,这样保证只创建一个对象
synchronized 可加在方法上也可加在代码块上
class Sync{
public synchronized void test() {
System.out.println("test开始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test结束..");
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
} 复制代码
运行结果:
并没有什么变化,并没有看到synchronized起到作用
那我们试试把synchronized加到代码块上,减小锁的粒度
class Sync {
public void test() {
synchronized (Sync.class) {
System.out.println("test开始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test结束..");
}
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
} 复制代码
运行结果:
test开始.. test结束.. test开始.. test结束.. test开始.. test结束..复制代码
由此可看出,synchronized锁的不是this,而是类的Class对象
Synchronized 的可重入性
总结
synchronized 锁住的要么是该类的实例,要么是传入的对象,还是那句话,synchronized使变量或代码块在某一时刻只能被一个线程访问