Synchronized
一、使用
修饰类的:实例方法、静态方法、代码块;
实例方法:锁对象为当前实例对象:
public synchronized void sayHello(){
System.out.println("Hello World");
}
静态方法:锁对象为当前类Class对象:
public static synchronized void sayHello(){
System.out.println("Hello World");
}
代码块:
//锁对象
synchronized(this){}
//锁对象
synchronized(""){}
//锁class
synchronized(xxx.class){}
二、原理
2.1、Java对象头(Object Header)
普通对象:
|--------------------------------------------------------------|
| Object Header (64 bits) |
|------------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|------------------------------------|-------------------------|
数组对象:
|--------------------------------------------------------------|
| Object Header (64 bits) |
|------------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|------------------------------------|-------------------------|
其中Mark Word(32位,JVM为32位):
|-------------------------------------------------------|--------------------|
| Mark Word (32 bits) | State |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 | Normal |
|-------------------------------------------------------|--------------------|
| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | Biased |
|-------------------------------------------------------|--------------------|
| ptr_to_lock_record:30 | lock:2 | Lightweight Locked |
|-------------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor:30 | lock:2 | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
| | lock:2 | Marked for GC |
|-------------------------------------------------------|--------------------|
- identity_hashcode:自身hashcode
- age:分代年龄,只有4位,最大值为15,所以-XX:MaxTenuringThreshold最大值为15;
- thread:偏向锁的线程ID
- lock::锁状态标记位,用尽可能少的二进制位表示尽可能多的信息。该标记的值不同,整个mark word表示的含义不同。
- biased_lock:是否启用偏向锁,1启用,0没有。
- state:
- Normal:正常(无状态)。lock:01;biased_lock:0
- Biased:偏向锁。lock:01;biased_lock:1
- Lightweight Locked:轻量级锁。lock:00;
- Heavyweight Locked:重量级锁。lock:10;
- Marked for GC:GC。lock:11;
2.2、Monitor 监视器
- WaitSet:线程调wait()时进入;
- EntryList:阻塞等待队列。
- Owner:获取锁的线程。
todo:原理过程:
三、内存可见性
synchronized能解决内存可见性问题,被synchronized加锁后,他会做出以下操作:
- 获取同步锁;
- 清空内存;
- 从主内存中拷贝新的对象副本到工作线程中;
- 继续执行代码,刷新主内存的数据;
- 释放同步锁;
四、总结:
- 可保证原子性;
- 可保证内存可见性;获取锁,从主内存获取最新;释放锁,把最新写入主内存;简单读取最新变量可用volatile;
- 注意死锁:A和B线程分别持有A和B锁,分别等待B和A锁;应按顺序申请;
- 可重入性:重入和退出时有计数器增减;
volatile关键字
内存可见性就是多个线程共享访问和操作相同的变量,但一个线程对一个共享变量的修改,另一个线程并不能马上被看到,甚至永远也看不到。
在计算机的系统中,除了内存。数据还会被缓存在CPU的寄存器以及各种缓存中,当访问一个变量时,可能直接从寄存器或CPU缓存中获取,而不一定到内存中去取,当修改一个变量时,也可能是先写到缓存中,稍后才会同步更新到内存中。在单线程的程序中,这一般不是问题。但是在多线程的程序中,尤其是在有很多CPU的情况下,这就是严重的问题。一个线程对内存的修改,另一个线程看不到,一是修改没有及时同步到内存,二是另一个线程根本就没从内存读。