最近遇到多线程处理的问题,原来只使用过synchronized的方法锁,对于其中的对象锁和类锁了解,但是没仔细研究过。所以回去查了相关资料进行整理。
基础知识
首先介绍一下对象锁(也叫方法锁)与类锁有那些不同。下文中使用对象锁称呼代替方法锁。
对于对象锁,是针对一个对象的,它只在该对象的某个内存位置声明一个标志位标识该对象是否拥有锁,所以它只会锁住当前的对象。一般一个对象锁是对一个非静态成员变量进行syncronized修饰,或者对一个非静态方法进行syncronized修饰。对于对象锁,不同对象访问同一个被syncronized修饰的方法的时候不会阻塞住。
类锁是锁住整个类的,当有多个线程来声明这个类的对象的时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁。这个时候在被阻塞住的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住。
无论是类锁还是对象锁,父类和子类之间是否阻塞没有直接关系。当对一个父类加了类锁,子类是不会受到影响的,相反也是如此。因为synchronized关键字并不是方法签名的一部分,它是对方法进行修饰的。当子类覆写父类中的同步方法或是接口中声明的同步方法的时候,synchronized修饰符是不会被自动继承的,所以相应的阻塞问题不会出现。
注意:这里的阻塞问题是指的按照正常情况下应该阻塞,而因为synchronized是父类与子类之间不可传递导致不会阻塞。那正常情况下阻塞是什么那,下面会详细介绍。但是,当一个子类没有覆盖父类的方法的时候,这时候通过子类访问方法则会产生阻塞。
插入一句:构造方法不可能是真正同步的(尽管可以在构造方法中使用同步块)。
下面截图给出了如何声明一个对象锁和如何声明一个类锁:
代码测试
如下面代码,我声明了一个类Runtest,在该类中包含无锁方法noSyn、对象锁方法 outMethod与一个类锁方法plus。声明了一个继承了线程Thread的类Obj,在该类中用来访问Runtest的方法,模拟各种测试场景。启动测试类是MutiThread,在该类中有两种测试方法,一种是声明同一个测试类对象而开辟多个线程,用来测试对象锁;另外一种是每当声明一个新的线程则同时声明一个新的测试类对象,用来测试类锁。
具体测试流程分为两个步骤。第一个步骤是直接运行如下代码,测试结果是用来测试对象锁的锁效果;第二个步骤是把for循环中的前两行代码注释掉,把其余三行有注释的代码删去注释,还有,类Obj最后一行注释代码删去注释,用来测试类锁的效果。
public class MutiThread {
public static void main(String[] args) {
Runtest runtest = new Runtest();
for (int i = 0; i < 10; i++) {
Thread thread = new Obj(runtest, i);// 1同一个RunTest1对象但每次有个新的线程
thread.start();
// Runtest rruntest = new Runtest(); //2 循环每次都声明一个新的对象
// Thread thread = new Obj(rruntest, i);
// thread.start();
}
}
}
class Obj extends Thread {
Runtest r;
int i = 0;
public Obj(Runtest r, int i) {
this.r = r;
this.i = i;
}
public void run() {
r.noSyn(this.getId());
//用以测试同一个对象在不同线程中访问不同方法
if(i % 2 == 0){
r.outMethod2();//对象锁方法2
}else{
r.outMethod();//对象锁方法1
}
//Runtest.plus(); //类锁方法
}
}
class Runtest {
static int i = 0;
public void noSyn(long threadId) {
System.out.println("nosyn: class obj->" + this + ", threadId->" + threadId);
}
synchronized public void outMethod() {
System.out.println("in outMethod begin");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
}
System.out.println("in outMethod end");
}
synchronized public void outMethod2() {
System.out.println("in outMethod2 begin");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
}
System.out.println("in outMethod2 end");
}
public static void plus() {
synchronized (Runtest.class) {
System.out.println("start: " + i);
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
}
i++;
System.out.println("i is " + i);
}
}
}
执行结果
按照上述测试步骤, 对于类锁的测试结果如下: