多线程的对象锁和类锁

    今天面试官问了一个问题,如果在一个类中的普通方法加上synchronized 关键字,两个线程可以同步方法这个方法吗?为什么

当时回答是不可以同步,然后面试官确认我说一定吗?当时就知道回答错了。

    现在实现了下,原来是类锁和对象锁的区别所在,也算是普及了下相关知识吧。

类锁:就是在方法前面加上 static synchronized或者方法里面 synchronized(MainThings.class) 这两个写法,那么这是一个类锁。

 

对象锁:就是在方法前面加上synchronized或者方法里面 synchronized(this) 这两个写法,那么这是一个类锁。

结论:如果是对象锁的话,两个线程可以同时访问同一个方法。

          如果是类锁的话,两个线程是不可以同时访问同一个方法,当前线程会将另外一个线程阻塞起来,等当前线程处理完了释放了当前锁后才可以进来。

原因:对象锁-》面对每一个对象来说,都有各自的锁,每个线程访问不同对象时都可以拥有自己的锁,所以不会造成这个线程的对象被另一个对象上锁。

          类锁-》对每个线程来说,只有一把锁,谁先拥有这把锁,谁先访问,其他线程阻塞,等当前访问完了的线程释放锁后,其他线程才可以访问。

 

public class DoThing1 implements Runnable {

private MainThings mainThings;

public DoThing1(MainThings mainThings)
{
this.mainThings=mainThings;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing1 is begin");
mainThings.test();

}

}

 

public class DoThing2 implements Runnable {

MainThings mainThings;

public DoThing2( MainThings mainThings)
{
this.mainThings=mainThings;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing2 is begin");
mainThings.test();

}

}

 

 

public class DoThing2 implements Runnable {

MainThings mainThings;

public DoThing2( MainThings mainThings)
{
this.mainThings=mainThings;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" DoThing2 is begin");
mainThings.test();

}

}

 

主类:

public class MainThings {

/**
* 对象锁
*/
public synchronized void test() {

System.out.println("this is"+ Thread.currentThread().getName()+" entry");

System.out.println("this is"+ Thread.currentThread().getName()+" out");



}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

MainThings mainThings=new MainThings();
MainThings mainThings2=new MainThings();
DoThing1 do1=new DoThing1(mainThings);
DoThing2 do2=new DoThing2(mainThings2);
Thread t1=new Thread(do1);
Thread t2=new Thread(do2);

t1.start();
t2.start();

}

}

 

 

执行结果:

Thread-1 DoThing2 is begin
Thread-0 DoThing1 is begin
this isThread-1 entry
this isThread-0 entry
this isThread-0 out
this isThread-1 out

 

类锁:

public static synchronized void test() {

System.out.println("this is"+ Thread.currentThread().getName()+" entry");

System.out.println("this is"+ Thread.currentThread().getName()+" out");



}

/**
* @param args
*/
public static void main(String[] args) {

// TODO Auto-generated method stub

MainThings mainThings=new MainThings();
MainThings mainThings2=new MainThings();
DoThing1 do1=new DoThing1(mainThings);
DoThing2 do2=new DoThing2(mainThings2);
Thread t1=new Thread(do1);
Thread t2=new Thread(do2);

t1.start();
t2.start();

}

执行结果:

Thread-0 DoThing1 is begin
this isThread-0 entry
this isThread-0 out
Thread-1 DoThing2 is begin
this isThread-1 entry
this isThread-1 out

PS:转载知乎上面大牛的对类锁和对象锁的解释:

作者:beralee
链接:https://www.zhihu.com/question/19708552/answer/12719903
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

要明白两个问题,1.锁的对象是谁,2.谁持有了锁。
假设方法A和B是在同一个类Test中的两个方法。

Test t=new Test();
t.methodB();

这个时候,methodB方法被调用时,因为加了synchronized ,需要先获得一个锁,这个锁的对象应该是t,也就是当前的这个Test类的实例,而获得锁的东西是线程,也就是说当前线程拿到了t的锁(而不是你说的B方法获得锁),这个时候B方法内调用methodA,因为A也加了synchronized,也需要获得一个锁,因为A和B都是Test类中的方法,所以当前线程要获得的锁的对象也是t。由于当前线程在执行B方法时已经持有了t对象的锁,因此这时候调用methodA是没有任何影响的,相当于方法A上没有加synchronized。

另一种情况:假设现在有两个Test类
Test t1=new Test();
Test t2=new Test();
t1.methodB();//此时当前线程持有了t1对象的锁
t2.methodB();//此时当前线程也持有了t2对象的锁
当前线程持有了两把锁,锁的对象分别是两个不同的Test类的实例t1和t2,互相没有影响。

再一种情况:假设在多线程环境下,两个线程都可以访问Test t=new Test();
此时假设thread1里调用t.methodB();同时thread2里调用t.methodB()

这时假设thread1先抢到t对象的锁,那么thread2需要等待thread1释放t对象的锁才可以执行B方法。
结果像这样:
thread1获得t的锁--thread1执行methodB--thread1执行methodA--释放t的锁---thread2获得t的锁--thread2执行methodB--thread2执行methodA--释放t的锁。

synchronized还有很多种使用方法,但只有明白是那条线程获得哪个对象的锁,就很容易明白了
 
 

 

posted @ 2018-03-17 13:10  xlh  阅读(325)  评论(0编辑  收藏  举报