Synchronized相关问题
Synchronized相关问题
Synchronized 锁信息是存储在哪里的?
-
当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的
-
当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的
-
当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的(不能修饰基本数据类型,不可修饰null对象,可以修饰 String obj = "123"、String obj ="")
怎么证明锁信息就是在那里的?
一、当 synchronized 修饰普通方法时,锁信息是存储在 this对象中头的
实验思路:
-
在使用锁之前,锁信息肯定没有存储,这时打印当前对象信息
-
在使用锁的时,锁的信息肯定已经存储,这时打印当前对象信息
-
在使用锁之后,锁的信息肯定已经消除,这时打印当前对象信息
预想结果:
如果第一步和第三步的对象头信息相同,第二步与第一、第三步的对象头信息不同
则证明对象头信息是存储在当前对象的对象头中
实验代码:
public class T001_Synchronized_Method {
synchronized void m() {
LockSupport.park();
}
public static void main(String[] args) throws InterruptedException {
T001_Synchronized_Method test = new T001_Synchronized_Method();
System.out.println("========== 加锁之前 ==========");
System.out.println(ClassLayout.parseInstance(test).toPrintable());
System.out.println("========== 加锁中 ==========");
Thread thread = new Thread(() -> test.m());
thread.start();
System.out.println(ClassLayout.parseInstance(test).toPrintable());
LockSupport.unpark(thread);
System.out.println("========== 释放锁之后 ==========");
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
}
实验结果:
========== 加锁之前 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
========== 加锁中 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) a0 49 26 0b (10100000 01001001 00100110 00001011) (187058592)
4 4 (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
========== 释放锁之后 ==========
com.yang.base.thread.A002_Synchronized.T001_Synchronized_This object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
实验结论:
我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的
二、当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的
实验思路:
-
在使用锁之前,锁信息肯定没有存储,这时打印当前类的Class 信息
-
在使用锁的时,锁的信息肯定已经存储,这时打印当前类的Class 信息
-
在使用锁之后,锁的信息肯定已经消除,这时打印当前类的Class 信息
预想结果:
如果第一步和第三步的对象头信息相同,第二步与第一、第三步的对象头信息不同
则证明对象头信息是存储在当前类的Class的对象头中
实验代码:
public class T001_Synchronized_Static_Method {
synchronized static void m() {
LockSupport.park();
}
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 加锁之前 ==========");
System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());
System.out.println("========== 加锁中 ==========");
Thread thread = new Thread(() -> m());
thread.start();
System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());
LockSupport.unpark(thread);
System.out.println("========== 释放锁之后 ==========");
System.out.println(ClassLayout.parseInstance(T001_Synchronized_Static_Method.class).toPrintable());
}
}
实验结果:
========== 加锁之前 ==========
java.lang.Class object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 a4 ba 5f (00000001 10100100 10111010 01011111) (1606067201)
4 4 (object header) 13 00 00 00 (00010011 00000000 00000000 00000000) (19)
8 4 (object header) df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......
========== 加锁中 ==========
java.lang.Class object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) b0 89 0a 0e (10110000 10001001 00001010 00001110) (235571632)
4 4 (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
8 4 (object header) df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......
========== 释放锁之后 ==========
java.lang.Class object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 a4 ba 5f (00000001 10100100 10111010 01011111) (1606067201)
4 4 (object header) 13 00 00 00 (00010011 00000000 00000000 00000000) (19)
8 4 (object header) df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
......
实验结论:
我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的
三、当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的
实验思路:
-
在使用锁之前,锁信息肯定没有存储,这时打印obj对象信息
-
在使用锁的时,锁的信息肯定已经存储,这时打印obj对象信息
-
在使用锁之后,锁的信息肯定已经消除,这时打印obj对象信息
预想结果:
如果第一步和第三步的obj对象头信息相同,第二步与第一、第三步的obj对象头信息不同
则证明对象头信息是存储在obj对象的对象头中
实验代码:
public class T001_Synchronized_Code_Block {
private static Object obj = new Object();
static void m() {
synchronized (obj){
LockSupport.park();
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 加锁之前 ==========");
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
System.out.println("========== 加锁中 ==========");
Thread thread = new Thread(() -> m());
thread.start();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
LockSupport.unpark(thread);
System.out.println("========== 释放锁之后 ==========");
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}
实验结果:
========== 加锁之前 ==========
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
========== 加锁中 ==========
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) a0 89 80 0b (10100000 10001001 10000000 00001011) (192973216)
4 4 (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
========== 释放锁之后 ==========
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
实验结论:
我们可以观察到对象头信息确实与我们预想的一样,证明:当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的
疑问:多线程是否同时可以访问同一个类中 不同 synchronized 修饰的方法?
-
当 synchronized 修饰普通方法时,锁信息是存储在 this 对象中头的
-
当 synchronized 修饰静态方法时,锁信息是存储在 当前类Class对象头中的
-
当 synchronized(obj) 修饰同步代码块时,锁信息是存储在obj对象头中的
如果1锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意synchronized修饰的普通方法
如果2锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意synchronized修饰的静态方法
如果3锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块
答案是肯定的可以的
一、如果1锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意synchronized修饰的普通方法
如果1锁定的是当前 this 对象,多个线程可以同时访问 不同this对象 的任意 synchronized 修饰的普通方法
实验思路:
- 线程1 访问 synchronized 修饰的普通方法 m()
- 在线程1运行过程中,线程2 访问 synchronized 修饰的普通方法 n()
- 在线程1运行过程中,线程3 访问普通方法 o()
- 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行
预想结果:
如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想
实验代码:
public class T001_Synchronized_Method_2 {
synchronized void m() {
System.out.println(Thread.currentThread().getName()+"进入方法 m ");
LockSupport.park();
}
synchronized void n() {
System.out.println(Thread.currentThread().getName()+"进入方法 n ");
LockSupport.park();
}
void o() {
System.out.println(Thread.currentThread().getName()+"进入方法 o ");
LockSupport.park();
}
public static void main(String[] args) throws InterruptedException {
T001_Synchronized_Method_2 test = new T001_Synchronized_Method_2();
Thread thread = new Thread(() -> test.m(),"线程1");
thread.start();
Thread.sleep(500);
Thread thread2 = new Thread(() -> test.n(),"线程2");
thread2.start();
Thread thread3 = new Thread(() -> test.o(),"线程3");
thread3.start();
}
}
实验结果:
线程1进入方法 m
线程3进入方法 o
实验结论:
我们可以观察到线程1 和线程3 正常运行,线程2 却迟迟不见输出,证明:如果锁定的是当前 this 对象,多个线程不可能同时访问 同一个this对象 的任意 synchronized 修饰的普通方法
二、如果2锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意synchronized修饰的静态方法
实验思路:
- 线程1 访问 synchronized 修饰的静态方法 m()
- 在线程1运行过程中,线程2 访问 synchronized 修饰的静态方法 n()
- 在线程1运行过程中,线程3 访问普通静态方法 o()
- 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行
预想结果:
如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想
实验代码:
public class T001_Synchronized_Static_Method_2 {
static synchronized void m() {
System.out.println(Thread.currentThread().getName()+"进入方法 m ");
LockSupport.park();
}
static synchronized void n() {
System.out.println(Thread.currentThread().getName()+"进入方法 n ");
LockSupport.park();
}
static void o() {
System.out.println(Thread.currentThread().getName()+"进入方法 o ");
LockSupport.park();
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> m(),"线程1");
thread.start();
Thread.sleep(500);
Thread thread2 = new Thread(() -> n(),"线程2");
thread2.start();
Thread thread3 = new Thread(() -> o(),"线程3");
thread3.start();
}
}
实验结果:
线程1进入方法 m
线程3进入方法 o
实验结论:
我们可以观察到线程1 和线程3 正常运行,线程2 却迟迟不见输出,证明:如果锁定的是当前类Class,多个线程不可能同时访问 同一个Class对象 的任意 synchronized 修饰的静态方法
三、如果3锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块
实验思路:
- 线程1 访问方法m() 其中包含 synchronized(obj) 修饰的代码块
- 在线程1运行过程中,线程2 访问方法n() 其中包含 synchronized(obj) 修饰的代码块
- 在线程1运行过程中,线程3 访问普通方法 o()
- 观察线程1和线程2是否同时运行,线程1和线程3 是否同时运行
预想结果:
如果线程1和线程2不能同时运行,线程1和线程3可以同时运行,则验证了我们的猜想
实验代码:
public class T001_Synchronized_Code_Block_2 {
private static Object obj = new Object();
void m() {
synchronized (obj){
System.out.println(Thread.currentThread().getName()+"进入方法 m ");
LockSupport.park();
}
}
static synchronized void n() {
synchronized (obj){
System.out.println(Thread.currentThread().getName()+"进入方法 n ");
LockSupport.park();
}
}
void o() {
System.out.println(Thread.currentThread().getName()+"进入方法 o ");
LockSupport.park();
}
public static void main(String[] args) throws InterruptedException {
T001_Synchronized_Code_Block_2 test = new T001_Synchronized_Code_Block_2();
Thread thread = new Thread(() -> test.m(),"线程1");
thread.start();
Thread.sleep(500);
Thread thread2 = new Thread(() -> test.n(),"线程2");
thread2.start();
Thread thread3 = new Thread(() -> test.o(),"线程3");
thread3.start();
}
}
实验结果:
线程1进入方法 m
线程3进入方法 o
实验结论:
我们可以观察到对象头信息确实与我们预想的一样,证明:如果锁定的是obj,多个线程不可能同时访问任意 synchronized(obj) 修饰的代码块