前段时间学了线程那章,对synchronized的关键字开始还搞得不太清楚,现在摸了几天总算大致了解了,现在用个人的理解和语言总结一下。
1.被synchronized作用的语句块,在线程调用执行时,这个语句块成为一个原子操作而不可分,避免出现脏数据(原子操作只是个比喻,中间还是能停止的,而脏数据是多线程为什么需要同步的根本原因),不能从运行到中间停止被别的操作同样语句块或者需要同样的对象锁的进程插进来运行。
2. synchronized(Object o),包括(this),更像是这个作用生效时,必须要满足的一个条件比如有个方法被一个线程启动,其线程的语句块里有一个synchronized(Object o){...},你想让语句块{...}执行,你必须有有对象o的锁,才能运行,因此,为了达成同步效果,往往随便造一个对象o就能达到锁(比如造一个对象lock,或者一个空的字符串)的目的,即同步的目的,此时除非语句块{...}释放了o的锁,如使用wait()语句,其他线程才有可能拿到o的锁,然后执行{...},这就达成了一个简单的同步手段。
3. 当synchronized修饰一个方法体synchronized a method(){...},或者说,也就是等价a method(){ synchronized(this){...}} 时(当然后者的粒度可以比前者的缩小一点,不一定包含整个方法体,只要包含需要同步的地方就行了),其实也一样,你要调用语句块{...}时,你必须要拿到this的锁,那this是指哪个对象呢?那就是调用这个方法或者这段语句块的对象
比如
class A{
int i=0;
public void add(){ //等价于public void add(){synchornized(this){i++};
i++;
try{
Thread.sleep(2);
}catch(Exception e){}
}
public synchronized void minus(){
i--;
try{
Thread.sleep(2);
}catch(Exception e){}
}
}
class Add implements Runnable{
A a;
Add(A a){
this.a = a;
}
public void run(){
a.add();
}
}
class Minus implements Runnable{
A a;
Minus(A a){
this.a = a;
}
public void run(){
a.minus();
}
}
public class MAIN{
public static void main(String[] args){
A a=new A();
Thread t1=new Thread(new Add(a));
Thread t2=new Thread(new Add(a));
Thread t3=new Thread(new Minus(a));
Thread t4=new Thread(new Minus(a));
t1.start();
t2.start();
t3.start();
t4.start();
try{
Thread.sleep(100);
}catch(Exception e){
}
System.out.println(a.i);
}
}
4.最后一种就是对整个类加锁,那就是说你要调用这个类的(应该是指static方法)方法,你必须拿到这个类锁,这是无视某个具体对象的。这里就不谈这个了。
===================
最后在针对之前说的第2点补充一下,第2点说到了“ synchornized(Object o),包括(this),更像是这个作用生效时,必须要满足的一个条件”,这个条件是针对这个线程来说的,只要这个线程竞争到了这个条件,那么在synchronized的语句块,若又调用其他的方法,这个方法也要这个锁,那么只要还在这个线程里,就没问题。
比如,在上面的代码中,在类Add的run()方法里,加了个锁synchornized(a){a.add();} ,但是运行a.add();也需要对象a的锁,我最初按照自己的理解,此时锁被Add的一个实例拿到了,那么对象a要运行add()方法就不行了。但做实验后发现我错了,因为这个条件是针对一个线程来说的,在java的实现中,是分配一个监视器到一个线程的,所以说只要是在一个线程,就没问题。