定义:它是解决的是多个线程之间访问同一个资源的同步性,它可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。简而言之,它提供的是一种排它的机制,任意时刻只能被一个线程所执行。
用法:
- Synchronized添加到代码块
//修饰代码块
public void test1(Object o) {
synchronized (o) {
//doing
}
}
synchronized如果锁的是某一个obj的对象,实际上作用在代码块上。
- synchronized添加在普通方法上
//修饰普通方法
public synchronized void test2() {
//doing
}
Synchronized加在普通方法上,锁的是对象实例。
多个线程来竞争时,哪个线程获取了该对象实例哪个线程调用的方法才能继续执行
- Synchronized添加到静态方法上
//修饰静态方法
public synchronized static void test3() {
//doingjava
}
如果Synchronized加在静态方法上,锁的就是当前的class实例 只有一个
特点:
- Synchronized修饰的方法或代码块,在同一时刻JVM只能允许一个线程访问
- Synchronized通过锁机制来完成同一时刻只能一个线程访问(临界区)
- 并发编程中,Synchronized的锁机制可以做到原子性,可见性,有序性
底层原理:
通过Javac命令将代码编程生成字节码文件.class文件
通过javap -v XXX.class反编译字节码
1.修饰代码块时使用monitorenter和monitorexit来完成,
同步代码块:
monitorenter
每一个对象都与一个monitor相关联,一个monitor的lock只能被一个线程在同一时间所
拥有,在一个线程尝试获取monitor的使用会发生以下事情:
a.如果monitor的entry count为0,意味monitor的lock还没有被获取,某个线程获取
之后会对entry count+1,从此该线程就是这个monitor的所有者了
b.如果一个已经拥有该monitor使用权的线程再次进入,会导致monitor的entry count+1
(可重入锁)
c.如果monitor已经被其他线程所拥有,其他线程尝试获取该monitor的所有权时,会被陷入阻塞,直到monitor的entry count为0,才能再次尝试去获取
monitorexit
释放对monitor的使用权,要释放对某个对象的monitor的使用权前提是首先获取该monintor的所有权,将monitor的entry count-1,如果entry count为0,那就表示该线程不再拥有该monitor的使用。
2.修饰静态方法flag中通过ACC_STATIC,ACC_SYNCHRONIZED标志修饰。
3.修饰普通方法flag中通过ACC_SYNCHRONIZED标志修饰。
同步方法:
常量池中多了ACC_SYNCHRONIZED标识符,标识当前的方法是一个同步方法,当方法被 调用,调用指令会检查方法ACC_SYNCHRONIZED标识符是否被设置,如果设置,执行线程会先去获取monitor,获取成功之后才会去执行方法体,方法体执行完成之后或释放monitor。
总结:无论使用以上哪种方式,本质上都是对一个对象的监视器(monitor)进行获取,而对于这个监视器的获取是排他的,也就是同一时刻一个线程可以获取到由Synchronized所保护的对象的监视器。
使用场景:
1、两个线程同时访问同一个对象的同步方法 : 安全(只有一把锁)
2、两个线程同时访问两个对象的同步方法 :不安全 test1.func1() test2.func1()
3、两个线程同时访问(一个或两个)对象的静态同步方法 :安全(静态方法只有一份)
4、两个线程分别同时访问(一个或两个)对象的同步方法和非同步方法 :
不安全 (同步方法会安全,非同步方法就会抢锁)
5、两个线程访问同一个对象中的同步方法,同步方法又调用另外一个非同步方法 :不安全
(非同步方法的执行会不安全)
6、两个线程同时访问同一个对象的不同的同步方法 :安全(一个对象一个锁)
7、两个线程同时访问静态synchronized和非静态synchornized方法 :不安全
(静态方法属于类,非静态方法属于对象。2者作用的对象都不同,所以不安全。)