今天面试官问了一个问题,若是在一个类中的普通方法加上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:转载知乎上面大牛的对类锁和对象锁的解释:
要明白两个问题,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的锁。