synchronized关键字

  synchronized关键字用于实现线程间同步,经过编译之后,会在同步块前后分别形成monitorenter和monitorexit这两个字节码指令。这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象。如果synchronized明确指定了对象参数,就使用这个对象的reference;如果没有明确指定,就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。

  在执行monitorenter指令时,首先要尝试获取对象的锁,如果这个对象没被锁定,或者当前线程已经拥有了这个对象的锁,就把锁的计数器加1;在执行monitorexit指令时会将计数器减1,当计数器为0时,锁就被释放。如果获取对象锁失败,就处于阻塞状态,直到被其他线程释放。

用法:

  1. 指定加锁对象:给某个对象加锁,进入同步代码前要获取这个对象的锁。注意:必须保证锁的是同一个对象实例,若两个线程运行时,某方法中锁的对象不是同一个,同步不起作用。

Test instance = new Test();
public void run(){
    synchronized(instance){
    //todo
    }  
} 

  2. 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。注意:必须保证操作的是同一个对象,若两个线程分别new了一个相同类的对象,同步不起作用。

public synchronized void run(){
    //todo
}

  3. 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。当两个线程分别new了一个相同类的对象,同步也起作用,因为锁的是这个 类。

public static synchronized void run(){
    //todo
}

常用集合:

ArrayList线程不安全,Vector线程安全

HashMap线程不安全,ConcurrentHashMap线程安全

StringBuilder线程不安全,StringBuffer线程安全

错误对Integer加锁:

public Integer i = 0;
synchronized(i){
   i++;  
}

例如上面代码,对i进行了加锁。但是这违背了用法1的规范,对指定对象加锁必须是同一个对象,当执行i++后,实际上是调用Integer.valueOf()新建了一个对象赋值给i(i = Integer.valueOf(i.intValue()+1)),此时i是一个新对象,所以每次加锁都加在了不同对象上。

  

原文地址:https://www.cnblogs.com/wwzyy/p/10096553.html