最近看视频学习java,这几天对synchronized关键字一直很迷惑,今天自己写了几个小程序验证,算是明白了在方法前加synchronized的作用
在方法前加synchronized关键字的效果:当同一个对象作为参数创建的不同线程调用带有synchronized方法时,存在互斥等待,也就是说用同一个对象产生的不同线程执行该方法时,他们之间必须只有一个可以执行,其他的必须等待当前这个执行完以后才能执行,所谓的不可打断是指不可以被同一个对象的线程打断,这里同一个对象至关重要,因为java中每一个对象都有自己的一个对象锁,当该对象的某个synchronized方法被调用后,可以理解为该方法把该对象的对象锁拿走了,此时此刻,该对象的线程无论是访问这个对象的这个方法或是其它方法,只要前边带有synchronized,都不能访问,必须等待,等待当前拿着对象锁的那个方法结束。简单来说,只要有一个synchronized方法在执行,那么该对象的其他带有synchronized的方法都不能执行,须等待,只要是同一个对象的调用,就是这样,和哪个线程没关系(也就是说无论是当前执行方法所在的线程还是其他用同一个对象new出来的线程)。不过,这个时候该对象还是可以访问那些不带有synchronized的方法的。所以对于共享变量所涉及的方法都要认真考虑。
来看一个例子程序
class Cat implements Runnable{
String voice="HH";
public Cat(){}
public Cat(String s){
this.voice=s;
}
public synchronized void run(){
System.out.println(voice);
try{
Thread.sleep(4000);}catch(Exception e){
e.printStackTrace();}
System.out.println("still"+voice);
voice="changed";
}
}
public class Test{
public static void main(String args[]){
Cat c1=new Cat("haha");
Cat c2=new Cat("hehe");
Thread t1=new Thread(c1);
Thread t2=new Thread(c1);
t1.start();
t2.start();
}
}
程序中t1 ,t2是用同一个对象创建的不同线程,他们访问带有synchronized方法run,如果他们之间是没有互斥等待的,那么结果应该为
haha
hehe
stillhaha
stillchanged
但是实际结果为
haha
stillhaha
changed
stillchanged
这说明第一个线程t1改变voice时影响了t2
再来说说不是同一个对象new出来的线程吧。他们之间访问同一个synchronized方法时没有任何限制,因为虽然是一个类的方法,但是来自不同的对象,大家各自访问各自的。再来看一个程序
class Cat implements Runnable{
static String voice="HH";
public Cat(){}
public Cat(String s){
this.voice=s;
}
public synchronized void run(){
System.out.println(voice);
try{
Thread.sleep(4000);}catch(Exception e){
e.printStackTrace();}
voice="changed";
System.out.println("still"+voice);
}
}
public class Test{
public static void main(String args[]){
Cat c1=new Cat();
Cat c2=new Cat();
Thread t1=new Thread(c1);
Thread t2=new Thread(c2);
t1.start();
t2.start();
}
}
t1和t2是由同一个类的不同对象创建出来的不同线程,他们共同调用run方法,其中有一个类变量voice,如果t1,t2是互斥调用的,那么结果应该为
HH
Stillchanged
changed
Stillchanged
但是实际的结果为
HH
HH
Stillchanged
Stillchanged
这说明第一个线程改变类变量voice时并没有影响到第二个线程,所以他们是不是互斥访问的。
总结:
1.对于同一个对象的任意(比如第一个程序中的t1和t2,都是由c1这同一个对象创建的)线程,访问带有synchronized方法时是互斥的,根本原因在于一个对象只有一个对象锁,这个锁在某个时间段只能由一个方法获得,当它执行完以后,才释放这把锁给其他的用。但是访问那些不带synchronized的方法则不受影响,所以对于共享变量之类的临界资源,要考虑清楚。
2.对于不同对象的线程,访问同一个synchronized方法,不受影响,因为此时有多于一把的对象锁。这个时候就要注意一下同一个类的不同对象对类变量的操作了,考虑好是不是对类变量要加锁