多线程的对象锁和类锁

    今天面试官问了一个问题,若是在一个类中的普通方法加上synchronized 关键字,两个线程能够同步方法这个方法吗?为何面试

当时回答是不能够同步,而后面试官确认我说必定吗?当时就知道回答错了。多线程

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

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

 

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

结论:若是是对象锁的话,两个线程能够同时访问同一个方法。对象

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

缘由:对象锁-》面对每个对象来讲,都有各自的锁,每一个线程访问不一样对象时均可以拥有本身的锁,因此不会形成这个线程的对象被另外一个对象上锁。get

          类锁-》对每一个线程来讲,只有一把锁,谁先拥有这把锁,谁先访问,其余线程阻塞,等当前访问完了的线程释放锁后,其余线程才能够访问。同步

 

public class DoThing1 implements Runnable {io

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还有不少种使用方法,但只有明白是 那条线程得到哪一个对象的锁,就很容易明白了
相关文章
相关标签/搜索