并发编程:同步方法的锁对象与锁竞争
1 示例描述
示例:定义线程任务类如下所示:
public class SynObj {
public synchronized void showA(){
System.out.println("showA..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void showB(){
synchronized (this) {
System.out.println("showB..");
}
}
public static synchronized void showC(){
System.out.println("showC..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void showD() {
synchronized (SynObj.class) {
System.out.println("showD..");
}
}
public void showE() {
System.out.println("showE..");
}
}
思考不同方法调用情境下的锁竞争情况。
2 同步方法的锁对象
观察以下示例代码:
public class Test {
public static void main(String[] args) {
final SynObj sy = new SynObj();
new Thread(new Runnable() {
@Override
public void run() {
sy.showA();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
sy.showB();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
sy.showE();
}
}).start();
}
}
程序运行输出:
showA..
showE..
showB.. //延时约3秒后输出
由 synchronized 关键字修饰的 同步方法 的锁对象是实例对象本身。 线程A抢到时间片后执行 showA() 方法,在方法体内执行 sleep() 方法休眠但不释放锁。而线程B执行 showB() 方法中的同步代码块使用的锁对象是 this 引用,也就是执行线程任务的实例对象本身。所以在线程A睡眠时,线程B由于无法拿到锁而阻塞。期间线程E抢得CPU执行权后得以继续执行任务,而线程B则要等待线程A执行完毕释放锁对象后才能继续执行。
3 静态同步方法的锁对象
观察以下示例代码:
public class Test {
public static void main(String[] args) {
final SynObj sy = new SynObj();
new Thread(new Runnable() {
@Override
public void run() {
SynObj.showC();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
sy.showD();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
sy.showE();
}
}).start();
}
}
程序运行输出:
showC..
showE..
showD.. //延时约3秒后输出
由 synchronized 关键字修饰的 静态同步方法 的锁对象是类对象(class)。 线程C抢到时间片后执行 showC() 方法,在方法体内执行 sleep() 方法休眠但不释放锁。而线程D执行 showD() 方法中的同步代码块使用的锁对象是 类名.class 引用,也就是执行线程任务的类对象。所以在线程C睡眠时,线程D由于无法拿到锁而阻塞。期间线程E抢得CPU执行权后得以继续执行任务,而线程D则要等待线程C执行完毕释放锁对象后才能继续执行。