一路谈谈锁

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加锁后,他会做出以下操作:

  1. 获取同步锁;
  2. 清空内存;
  3. 从主内存中拷贝新的对象副本到工作线程中;
  4. 继续执行代码,刷新主内存的数据;
  5. 释放同步锁;

四、总结:

  • 可保证原子性;
  • 可保证内存可见性;获取锁,从主内存获取最新;释放锁,把最新写入主内存;简单读取最新变量可用volatile;
  • 注意死锁:A和B线程分别持有A和B锁,分别等待B和A锁;应按顺序申请;
  • 可重入性:重入和退出时有计数器增减;

volatile关键字

内存可见性就是多个线程共享访问和操作相同的变量,但一个线程对一个共享变量的修改,另一个线程并不能马上被看到,甚至永远也看不到。

在计算机的系统中,除了内存。数据还会被缓存在CPU的寄存器以及各种缓存中,当访问一个变量时,可能直接从寄存器或CPU缓存中获取,而不一定到内存中去取,当修改一个变量时,也可能是先写到缓存中,稍后才会同步更新到内存中。在单线程的程序中,这一般不是问题。但是在多线程的程序中,尤其是在有很多CPU的情况下,这就是严重的问题。一个线程对内存的修改,另一个线程看不到,一是修改没有及时同步到内存,二是另一个线程根本就没从内存读。

上一篇:【MySQL】《高性能MySql》笔记二


下一篇:CF1504C Balance the Bits