通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
具体的解释:
1、当thread01执行getNumByUserName方法碰到synchronized关键字时,执行monitorenter指令,thread01去获取与threadSynch对象关联的monitor(监视器)的entry count,此时与threadSynch对象关联的monitor(监视器)的entry count是0,所以thread01进入该监视器并将其entry count设置为1,thread01此时是与threadSynch对象关联的monitor(监视器)的所有者。
2、thread01执行getNumByUserName方法sleep的时候,thread02进来执行该方法,但如上所述,thread01此时是与threadSynch对象关联的monitor(监视器)的所有者,thread02进入阻塞状态。
3、thread01执行getNumByUserName方法结束,执行monitorexit指令,thread01将与threadSynch对象关联的monitor(监视器)的entry count减1,此时entry count为0,thread01退出monitor,不在是其所有者。
4、处于阻塞状态的thread02重新进入与threadSynch对象关联的monitor(监视器),获取所有权,此时与threadSynch对象关联的monitor(监视器)的entry count是0,接下来的步骤与1中相同。
3.2 synchronized修饰普通方法
public classThreadSynch {private intnum;public synchronized voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
反编译结果:
从反编译结果来看,方法的同步并没有通过monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
3.3 synchronized修饰静态方法
public classThreadSynch {private static intnum;public static synchronized voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
反编译结果:
可以看到,静态方法的同步和普通方法的同步其实现方式都是一样的。
通过实例验证:
① 多线程访问同一对象-----同一方法-----不加锁
上述的多线程安全问题实例已经演示过了。
② 多线程访问同一对象-----同一方法-----加锁
上述讲解synchronized原理已经演示过了。
③ 多线程访问同一对象-----不同方法-----两个方法都不加锁
为了方便,两个方法操作的是不同的属性
线程:thread01、thread02
同一对象:ThreadSynch
两个方法:getNumByUserName、getIDByUserName
线程访问的对象:
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}public voidgetIDByUserName(String userName){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("zs");
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getIDByUserName("zs");
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch= newThreadSynch();//两个线程访问同一个对象
Thread thread01 = newThread01(threadSynch);
Thread thread02= newThread02(threadSynch);
thread01.start();
thread02.start();
}
}
结果:
zs setNum 100zs setID1zs num======100zs id======1
说明:两个线程访问同一个对象的不同方法,两个方法都没有加锁,那么两个线程就不存在一个等待另一个执行完再执行的问题,即各执行各的,也不存在线程安全问题。
④ 多线程访问同一对象-----不同方法-----两个方法都加锁
线程:thread01、thread02
同一对象:ThreadSynch
加锁的两个方法:getNumByUserName、getIDByUserName
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}public voidgetIDByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
}
结果:
zs setNum 100zs num======100zs setID1zs id======1
说明:两个线程访问同一对象的不同方法,这两个方法都加了锁,那么thread01执行getNumByUserName方法的时候,根据synchronized的原理,thread01是与threadSynch对象关联的monitor(监视器)的所有者,该monitor的entry count是1,所以虽然thread02执行的是另一个方法getIDByUserName,但还是处于阻塞状态,必须等待thread01执行完后,不再是monitor的所有者,thread02才能执行。
⑤ 多线程访问同一对象-----不同方法-----一个方法加锁,一个方法不加锁
线程:thread01、thread02
同一对象:ThreadSynch
加锁的方法:getNumByUserName
不加锁的方法:getIDByUserName
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}public voidgetIDByUserName(String userName){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
结果:
zs setNum 100zs setID1zs id======1zs num======100
说明:两个线程访问同一对象的不同方法,getNumByUserName方法加了锁,getIDByUserName方法没加锁。那么thread01执行getNumByUserName方法的时候,根据synchronized的原理,先判断与threadSynch对象关联的monitor(监视器)是否被哪个线程所有,没有,thread01就是与threadSynch对象关联的monitor的所有者,该monitor的entry count是1。thread02执行getIDByUserName方法,因为该方法没有加锁,所以无需判断与threadSynch对象关联的monitor是否被哪个线程所有(此时该monitor被thread01所有,但与thread02无关),即thread01和thread02各执行各的,无需等待哪一方执行结束在执行。
⑥ 多线程访问不同对象-----同一方法-----不加锁
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
不加锁的方法:getNumByUserName
线程访问的对象:
public classThreadSynch {private intnum;public voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("zs");
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("ls");
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch1= newThreadSynch();
ThreadSynch threadSynch2= newThreadSynch();//两个线程访问两个不同的对象
Thread thread01 = newThread01(threadSynch1);
Thread thread02= newThread02(threadSynch2);
thread01.start();
thread02.start();
}
}
结果:
zs setNum 100ls setNum200ls num======200zs num======100
说明:两个线程访问不同对象(同一个类)的同一方法,getNumByUserName方法没有加锁。方法没加锁,说明不需要判断与threadSynch对象关联的monitor(监视器)被谁所有,两个线程各执行各的,所操作的num属性也是自己对象中的。
⑦ 多线程访问不同对象-----同一方法-----加锁
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
加锁的方法:getNumByUserName
public classThreadSynch {private intnum;public voidgetNumByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
}
结果:
zs setNum 100ls setNum200ls num======200zs num======100
说明:两个线程访问不同对象的同一方法,getNumByUserName方法加了锁。那么thread01执行getNumByUserName方法的时候,根据synchronized的原理,先判断与threadSynch1对象关联的monitor(监视器)是否被哪个线程所有,没有,thread01就是与threadSynch1对象关联的monitor的所有者,该monitor的entry count是1。与此同时,thread02执行getNumByUserName方法,根据synchronized的原理,先判断与threadSynch2对象关联的monitor(监视器)是否被哪个线程所有,没有,thread02就是与threadSynch2对象关联的monitor的所有者,该monitor的entry count是1。如上,因为thread01和thread02访问的是两个不同的对象,各自获取与各自对象相关的monitor的所有权,所以两个线程各执行各的,不存在一个线程等待另一个线程的问题。
⑧ 多线程访问不同对象-----不同方法-----都不加锁
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
都不加锁的方法:getNumByUserName,getIDByUserName
线程访问的对象:
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}public voidgetIDByUserName(String userName){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("zs");
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getIDByUserName("ls");
}
}
测试:
zs setNum 100ls setID2ls id======2zs num======100
说明:两个线程访问不同对象的不同方法,getNumByUserName方法和getIDByUserName方法都没加锁。那么thread01执行getNumByUserName方法的时候,无需判断与threadSynch1对象关联的monitor(监视器)是否被哪个线程所有,类似的,thread02执行getIDByUserName方法,也无需判断与threadSynch2对象关联的monitor(监视器)是否被哪个线程所有,所以两个线程各执行各的。
⑨ 多线程访问不同对象-----不同方法-----都加锁
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
都加锁的方法:getNumByUserName,getIDByUserName
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}public voidgetIDByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
}
测试:
zs setNum 100ls setID2ls id======2zs num======100
说明:两个线程访问不同对象的不同方法,getNumByUserName方法和getIDByUserName方法都加锁。那么thread01执行getNumByUserName方法的时候,先判断与threadSynch1对象关联的monitor(监视器)是否被哪个线程所有,没有,thread01是与threadSynch1对象关联的monitor的所有者,该monitor的entry count是1,类似的,thread02执行getIDByUserName方法,先判断与threadSynch2对象关联的monitor(监视器)是否被哪个线程所有,没有,thread02是与threadSynch2对象关联的monitor的所有者,该monitor的entry count是1,因为thread01和thread02获取的是各自对象关联的monitor的所有权,所以两个线程各执行各的。
⑩ 多线程访问不同对象-----不同方法-----一个方法加锁,一个方法不加锁
线程:thread01、thread02
不同对象:threadSynch1,threadSynch2
加锁的方法:getNumByUserName
不加锁的方法:getIDByUserName
public classThreadSynch {private intnum;private intid;public voidgetNumByUserName(String userName){synchronized(this){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}public voidgetIDByUserName(String userName){if("zs".equals(userName)){try{
id= 1;
System.out.println("zs setID 1");
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
id= 2;
System.out.println("ls setID 2");
}
System.out.println(" " + userName + " id ======" +id);
}
}
测试:
zs setNum 100ls setID2ls id======2zs num======100
说明:两个线程访问不同对象的不同方法,getNumByUserName方法加锁,getIDByUserName方法没加锁。那么thread01执行getNumByUserName方法的时候,先判断与threadSynch1对象关联的monitor(监视器)是否被哪个线程所有,没有,thread01是与threadSynch1对象关联的monitor的所有者,该monitor的entry count是1,thread02执行getIDByUserName方法,该方法没加锁,无需判断与threadSynch2对象关联的monitor(监视器)是否被哪个线程所有,所以两个线程各执行各的。
四、Synchronized的可重入性
synchronized拥有锁重入的功能,所谓锁重入的意思就是:当一个线程得到一个对象锁后,再次请求该对象锁时可以再次得到该对象锁。这也证明在一个Synchronized方法/块的内部调用本类的其他Synchronized方法/块的时候,是永远可以得到锁的,因为锁都是同一个对象锁。
举例:
public classThreadSynch {public synchronized voidprint1(){
System.out.println("print1========");
print2();
}public synchronized voidprint2(){
System.out.println("print2========");
print3();
}public synchronized voidprint3(){
System.out.println("print3========");
}
}
thread01中调用上述对象的print1()方法
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.print1();
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch= newThreadSynch();
Thread thread01= newThread01(threadSynch);
thread01.start();
}
}
结果:
print1========print2========print3========
说明:thread01执行同步方法print1的时候,根据synchronized的原理,thread01先判断与threadSynch对象关联的monitor(监视器)是否被哪个线程所有,没有,thread01就是与threadSynch对象关联的monitor(监视器)的所有者,该monitor的entry count是1。
同步方法print1中调用同步方法print2,根据synchronized的原理,thread01先判断与threadSynch对象关联的monitor(监视器)是否被哪个线程所有,被自己占有,那就可以重新进入该monitor,其entry count加一变成2。
同步方法print2中调用同步方法print3,根据synchronized的原理,thread01先判断与threadSynch对象关联的monitor(监视器)是否被哪个线程所有,被自己占有,那就可以重新进入该monitor,其entry count加一变成3。
同步方法print3执行结束,与threadSynch对象关联的monitor(监视器)的entry count减一变成2。
同步方法print2执行结束,与threadSynch对象关联的monitor(监视器)的entry count减一变成1。
同步方法print1执行结束,与threadSynch对象关联的monitor(监视器)的entry count减一变成0。thread01不在是与threadSynch对象关联的monitor(监视器)的所有者。
这种锁重入的机制,也支持在父子类继承的环境中。子类同步方法中可以通过“锁重入”调用父类的同步方法。
五、异常自动释放锁
当一个线程执行的代码发生异常时,其所占有的锁会自动释放。
举例:两个线程访问同一对象的加锁方法
public classThreadSynch {public synchronized voidexceptionTest(){int num = 10000;
System.out.println("currentThread ===" +Thread.currentThread().getName());try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}while(true){int n = 2 /num;
num--;
}
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.exceptionTest();
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.exceptionTest();
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch= newThreadSynch();
Thread thread01= newThread01(threadSynch);
Thread thread02= newThread02(threadSynch);
thread01.start();
thread02.start();
}
}
结果:
currentThread ===Thread-0Exception in thread"Thread-0" java.lang.ArithmeticException: /by zero
at com.dajia.test.ThreadSynch.exceptionTest(ThreadSynch.java:23)
at com.dajia.test.Thread01.run(Thread01.java:26)
currentThread===Thread-1Exception in thread"Thread-1" java.lang.ArithmeticException: /by zero
at com.dajia.test.ThreadSynch.exceptionTest(ThreadSynch.java:23)
at com.dajia.test.Thread02.run(Thread02.java:25)
说明:thread01进来后,打印出“currentThread ===Thread-0”后,sleep了两秒,因为sleep方法并不释放锁,所以此时thread02还处于阻塞状态,直到thread01继续执行,抛异常后释放锁,thread02才开始执行。
六、Synchronized用法总结
synchronized的作用主要有三个:
1、确保线程互斥的访问同步代码
2、保证共享变量的修改能够同步可见
3、有效解决重排序问题
从语法上讲,Synchronized总共有三种用法:
1、修饰普通方法
2、修饰静态方法
3、修饰代码块
synchronized修饰代码块上面列举的实例全部都是,下面说一下另外两种的用法:
1 修饰普通方法
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
加锁的方法:getNumByUserName
public classThreadSynch {private intnum;public synchronized voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("zs");
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("ls");
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch1= newThreadSynch();
ThreadSynch threadSynch2= newThreadSynch();//两个线程访问两个不同的对象
Thread thread01 = newThread01(threadSynch1);
Thread thread02= newThread02(threadSynch2);
thread01.start();
thread02.start();
}
}
结果:
zs setNum 100ls setNum200ls num======200zs num======100
说明:多线程访问不同对象加锁的方法时,因为各自获取与各自对象关联的那个monitor,所以各执行各的,不存在一个等待另一个的问题。
2 修饰静态方法
线程:thread01、thread02
不同对象:threadSynch01,threadSynch02
加锁的方法:getNumByUserName
线程访问的对象:
public classThreadSynch {private static intnum;public static synchronized voidgetNumByUserName(String userName){if("zs".equals(userName)){try{
num= 100;
System.out.println("zs setNum 100");
Thread.sleep(5000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}else if("ls".equals(userName)){
num= 200;
System.out.println("ls setNum 200");
}
System.out.println(" " + userName + " num ======" +num);
}
}
thread01:
public class Thread01 extendsThread{privateThreadSynch threadSynch;publicThread01(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("zs");
}
}
thread02:
public class Thread02 extendsThread{privateThreadSynch threadSynch;publicThread02(ThreadSynch threadSynch) {this.threadSynch =threadSynch;
}
@Overridepublic voidrun() {
threadSynch.getNumByUserName("ls");
}
}
测试:
public classTest {public static voidmain(String[] args) {
ThreadSynch threadSynch1= newThreadSynch();
ThreadSynch threadSynch2= newThreadSynch();//两个线程访问两个不同的对象
Thread thread01 = newThread01(threadSynch1);
Thread thread02= newThread02(threadSynch2);
thread01.start();
thread02.start();
}
}
结果:
zs setNum 100zs num======100ls setNum200ls num======200
说明:对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),虽然threadSynch1和threadSynch2是两个不同的对象,但都是ThreadSynch这个类的实例,thread01执行getNumByUserName方法的时候,会获取与ThreadSynch这个类关联的monitor的所有权,此时thread02执行getNumByUserName方法的时候,也会获取与ThreadSynch这个类关联的monitor的所有权,但该monitor已经被thread01所有,所以thread02只能进入阻塞状态,等待thread01执行完释放monitor的所有权后,才能执行getNumByUserName方法。
参考资料: