非静态方法的锁 是 实例对象本身 (this),
静态方法的锁 是 类对象本身 (.class)
一、代码
package com.aop8.testJava;
import java.util.concurrent.TimeUnit;
/**
*
* <pre>
* 1. 两个普通同步方法,两个线程,标准打印,打印? //one two
* 2. 新增 Thread.sleep() 给getOne() ,打印? // one two
* 3. 新增普通方法getThree() ,打印? //three one two
* 4. 两个普通同步方法,两个Number对象,打印? //two one
* 5. 修改getOne()为静态同步方法,打印? //two one
* 6. 修改两个方法均为静态同步方法,一个Number对象 ,打印? //one two
* 7. 一个静态同步方法,一个非静态同步方法,两个Number对象? //two one
* 8. 两个静态同步方法,两个Number对象? //one two
*
* 线程八锁的关键:
* 1. 非静态同步方法的锁默认为this (即实例对象本身),静态同步方法的锁为 .class (即类对象本身)
* 2. 某一个时刻内,只能有一个线程持有锁,无论有几个方法
*
* 总述:
* 1. synchronized 修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。
* 2. synchronized 修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。
* </pre>
*
*/
public class TestThread8Monitor {
public static void main(String[] args) {
final Number number=new Number();
final Number number2=new Number();
new Thread(new Runnable(){
public void run() {
number.getOne();
}
}).start() ;
new Thread(new Runnable(){
public void run() {
number.getTwo();
//number2.getTwo();
}
}).start();
// new Thread(new Runnable(){
// public void run() {
// number.getThree();
// }
// }).start() ;
}
}
class Number {
public static synchronized void getOne() {
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public synchronized void getTwo(){
System.out.println("two");
}
public synchronized void getThree(){
System.out.println("three");
}
}
二、理论说明
1. synchronized 修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。
Java中每个对象都有一个锁,并且是唯一的。假设分配的一个对象空间,里面有多个方法,相当于空间里面有多个小房间,如果我们把所有的小房间都加锁,因为这个对象只有一把钥匙,因此同一时间只能有一个人打开一个小房间,然后用完了还回去,再由JVM 去分配下一个获得钥匙的人。
情况1:同一个对象在两个线程中分别访问该对象的两个同步方法
结果:会产生互斥。
解释:因为锁针对的是对象,当对象调用一个synchronized方法时,其他同步方法需要等待其执行结束并释放锁后才能执行。
情况2:不同对象在两个线程中调用同一个同步方法
结果:不会产生互斥。
解释:因为是两个对象,锁针对的是对象,并不是方法,所以可以并发执行,不会互斥。形象的来说就是因为我们每个线程在调用方法的时候都是new 一个对象,那么就会出现两个空间,两把钥匙,
2. synchronized 修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。
情况1:用类直接在两个线程中调用两个不同的同步方法
结果:会产生互斥。
解释:因为对静态对象加锁实际上对类(.class)加锁,类对象只有一个,可以理解为任何时候都只有一个空间,里面有N个房间,一把锁,因此房间(同步方法)之间一定是互斥的。
注:上述情况和用单例模式声明一个对象来调用非静态方法的情况是一样的,因为永远就只有这一个对象。所以访问同步方法之间一定是互斥的。
情况2:用一个类的静态对象在两个线程中调用静态方法或非静态方法
结果:会产生互斥。
解释:因为是一个对象调用,同上。
情况3:一个对象在两个线程中分别调用一个静态同步方法和一个非静态同步方法
结果:不会产生互斥。
解释:因为虽然是一个对象调用,但是两个方法的锁类型不同,调用的静态方法实际上是类对象在调用,即这两个方法产生的并不是同一个对象锁,因此不会互斥,会并发执行。
3. 总结:
1.对象锁钥匙只能有一把才能互斥,才能保证共享变量的唯一性
2.在静态方法上的锁,和 实例方法上的锁,默认不是同样的,如果同步需要制定两把锁一样。
3.关于同一个类的方法上的锁,来自于调用该方法的对象,如果调用该方法的对象是相同的,那么锁必然相同,否则就不相同。比如 new A().x() 和 new A().x(),对象不同,锁不同,如果A的单利的,就能互斥。
4.静态方法加锁,能和所有其他静态方法加锁的 进行互斥
5.静态方法加锁,和xx.class 锁效果一样,直接属于类的