一个对象对应一把锁,要线程同步需要两者同步为同一对象。在该问题中,synchronized修饰pubulic方法中没有具体的参数默认的锁为this,即是当前实例对象。在创建过程中创建的是两个不同对象,对应两把不同的锁。由于调用时由于用的不是同一个锁,所以不能线程同步。
synchronized修饰的静态方法,能构成线程同步。静态方法加的锁为类对象的锁。由于静态方法具有全局唯一性,调用的时候调用的是同一对象,同一把锁,所以能线程同步。
synchronized修饰代码块有两种锁,一种是对象锁,另外一种是类锁。或者叫实例锁,全局锁。如果采用的是对象锁,需要对象为同一对象才能构成线程同步。如果用的是类锁,而类锁在虚拟机中具有唯一性,自然能构成线程同步。
1. 用在对象
Lock LOCK = new Lock();
/**
* 用在对象
*/
private void synchronizedInstance() {
synchronized (LOCK) {
System.out.println("synchronizedInstance");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以LOCK 为对象锁了。
/**
* 用在this
*/
private void synchronizedThis() {
synchronized (this) {
System.out.println("synchronizedThis");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
当前实例为锁。
对象锁,只有获取当前对象才能进入该方法里面。synchronized methods(){} 与synchronized(this){}之间没有什么区别。
私有锁,在类内部声明一个私有属性如private Object lock,在需要加锁的同步块使用 synchronized(lock)。
使用私有锁可以减小锁的细粒度,减少由锁产生的开销。私有锁其实也是对象锁的一种。
私有锁还可以这样用:用私有锁实现的等待/通知机制。
Object lock = new Object();
// 由等待方线程实现
synchronized (lock) {
while (条件不满足) {
lock.wait();
}
}
// 由通知方线程实现
synchronized (lock) {
条件发生改变
lock.notify();
}
2. 用在方法里
/**
* 用在普通方法
*/
private synchronized void synchronizedMethod() {
System.out.println("synchronizedMethod");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
如果不是单例,同步方法锁将失效。实际上是当前实例作为锁。
/**
* 用在静态方法
*/
private synchronized static void synchronizedStaticMethod() {
System.out.println("synchronizedStaticMethod");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
静态方法具有全局唯一性,类级别的锁,作为锁线程安全。
3. 用在类里
/**
* 用在类
*/
private void synchronizedClass() {
synchronized (Sync.class) {
System.out.println("synchronizedClass");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 用在类
*/
private void synchronizedGetClass() {
synchronized (this.getClass()) {
System.out.println("synchronizedGetClass");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
两个都是类级锁,线程安全。
类锁:使用 synchronized 修饰静态的方法以及 synchronized(class) 同步代码块使用的锁是类锁。
对象锁:使用 synchronized 修饰非静态的方法以及 synchronized(this) 同步代码块使用的锁是对象锁。
类锁和对对象锁不相互阻塞,相同的类锁或相同的实例锁相互阻塞。方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。保证了同一时刻只有一个线程可执行,从而有效的避免了类成员变量的访问冲突。