实现原理
synchronized采用的是锁机制,java中每个对象都有一个锁,在同一时刻,只有一个线程可以获得该锁,其他线程想要获取该锁的话,必须等到已经获取该锁的线程释放锁之后,因此synchronized正是通过获取对象的锁来保证不同线程的同步。
synchronized可以获取两种对象的锁:
- 实例对象的锁:即java类的实例
- 类的class对象
public class B { public void method(){ synchronized (this){ //获取类的实例的锁 } synchronized (B.class){ //获取类的calss对象的锁 } } }
synchronized可以修饰的位置
- 修饰静态方法,获取的是类的class对象的锁
- 修饰非静态方法,获取的是类的实例的锁
- 修饰代码块,这时候可以获取上述的两种锁,要视具体情况,如果作用于instance,那么获取的就是类的实例的锁,如果作用于class对象,那么获取的就是类的class对象的锁
public class B { public void method(){ /* 修饰代码块 */ synchronized (this){ //获取类的实例的锁 } synchronized (B.class){ //获取类的calss对象的锁 } } /* 修饰静态方法,获取的是类的class对象的锁 */ public synchronized static void staticOutput(){ } /* 修饰非静态方法,获取的是类的实例的锁 */ public synchronized void output(){ } }
详细介绍
获取instance锁
当synchronized获取instance锁时
/*
修饰非静态方法,获取的是类的实例的锁
*/
public synchronized void output(){
}
/*
修饰非静态方法,获取的是类的实例的锁
*/
public synchronized void intput(){
}
public void method(){
/*
修饰代码块
*/
synchronized (this){
//获取类的实例的锁
//C代码块
}
synchronized (B.class){
//获取类的calss对象的锁
//D代码块
}
}
- 如果是相同的实例,那么多个线程同时只能有一个线程执行synchronized修饰的方法或者代码块,并且如果线程1此时正在执行
output()
,那么线程2是不能够同时执行input()
,也不能同时执行method()
的C代码块,因为这时候synchronized获取的是这个instance的锁(唯一) - 如果是相同的实例,如果线程1此时正在执行
output()
,那么线程2可以执行非synchronized修饰的方法或者代码块 - 如果不是相同的实例,那么在不同线程执行到synchronized时,是没有影响的,即只要这两个线程的instance不同,那么线程1和线程2可以同时执行
output()
intput()
以及C代码块
,
也就是说synchronized的机制主要跟获取的哪个对象的锁有关,跟修饰的位置没有关系,对象不一致,那么获取的锁就不一致,那么也就没法保证不同线程的同步
获取类class锁
当synchronized修饰static方法或者后跟XXX.class
时,表示执行修饰位置的代码需要获取类的class对象锁
public void method(){
/*
修饰代码块
*/
synchronized (this){
//获取类的实例的锁
//C代码块
}
synchronized (B.class){
//获取类的calss对象的锁
//D代码块
}
}
/*
修饰静态方法,获取的是类的class对象的锁
*/
public synchronized static void staticOutput(){
}
- 同一时刻,只有一个线程能执行synchnized修饰的方法,线程1和线程2不能同时执行
staticOutPut()
,不管调用方式,B.staticOutput()
和B b = new B();b.staticOutput()
一样不可以同时执行,因为要调用staticOutPut()
需要获取B.class的锁 - 同一时刻,线程1执行
staticOutPut()
时,线程2不会执行上述代码中的D代码块,因为获取的都是同一个B.class的锁
synchronized能否保证原子性可见性有序性
原子性
因为synchronized使用的互斥锁机制,因此,它能够保证synchronized修饰的代码块的的操作是原子性的,也就是说他能保证在修饰位置代码执行过程中,其他线程想要执行这段代码的话,都必须等待线程1释放锁
可见性
在线程1执行到C代码块时之前的synchronized代码块时,会拷贝一份i的副本,同时线程2这时候也执行到这里时,也会拷贝副本,但是会阻塞,当线程1执行完时,由于synchronized保证了可见性,所以在线程执行完+1操作后,线程2会知道i的值在主内存中已经变化,会重新从主存中取i的值然后进行操作,也就是说线程2能知道线程1执行完后的操作结果
int i = 1;
public void method(){
/*
修饰代码块
*/
synchronized (this){
//获取类的实例的锁
//C代码块
i++;
System.out.println("i="+i);
}
/*synchronized (B.class){
//获取类的calss对象的锁
//D代码块
}*/
}
有序性
synchronized不能保证有序性,要保证有序性,配合valotile关键字