synchronized关键字的使用及互斥锁的实现

synchronized关键用于多线程环境中实现操作的原子性、互斥性,确保有序的同一时间对同一资源访问。实际上,是锁的一种实现。

用法:

class ClassA{

  synchronized void methodA(){//修饰非静态方法1

//临界区

}

synchronized static void methodB(){//修饰静态方法2

//临界区

}

扫描二维码关注公众号,回复: 7778137 查看本文章

void methodC(){

synchronized(){

//修饰代码块3,相较于修饰方法,影响的范围更小

}

}

}

修饰非静态方法时,锁定的对象是当前类的实例,修饰静态方法时,锁定的是当前类。对应于以上的1、2、3即。

synchronized(this) void …

synchronized(ClassA.class) void …

synchronized(this)…

this指的是类的一个实例,所以如果是不同实例,那么锁是无效的,无法做到资源访问的互斥。

因此,尽管在同一个类中都使用了synchronized关键字修饰资源,有时也无法做到安全的访问资源。因为资源对应的锁不是同一个。

多个相关联的资源应该作为同一个资源对应同一把锁。

例如

class ClassA{

  static int val=0;

  synchronized int getVal(){

    return val;

}

synchronized static void addVal(){

  val+=1;

}

}

以上是不安全的,因为两个synchronized所加的锁实际是不同的,不能实现资源访问的互斥。

应当改为:

class ClassA{

  static int val=0;

private final Object valLock=new Object();

  int getVal(){

    synchronized(valLock){

return val;

}

}

static void addVal(){

  synchronized(valLock){

    val+=1;

}

}

}

下面再看一个转账的例子:A账户往B账户转账100,B账户往C账户转账100,每一个账户原始金额为200,线程1负责A到B,线程2负责B到C,由于线程1只对A对象加锁,线程2只对B对象加锁,两者别不互斥,可能导致两个线程读取到B中余额都是200,而最终B中余额是300.

class Account {

  private int balance;

  // 转账

  synchronized void transfer(Account target, int amt){

    if (this.balance > amt) {

      this.balance -= amt;

      target.balance += amt;

    }

  }

}

因此上面的方式不能实现线程操作的安全,应当改为如下

class Account {
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    synchronized(Account.class) {
      if (this.balance > amt) {
        this.balance -= amt;
        target.balance += amt;
      }
    }
  } 
}

对类加锁,这样所有账户对象用同一把锁。

即使如此,可以实现多线程的安全操作,实际上依然是存在问题的。

如果Account类有多个子类AccountSon1 AccountSon2…,那么这些子类既属于自己的类型也同时是Account类型,在调用transfer方法时,上锁的实际是AccountSon1.class AccountSon2.class类型,依然存在安全问题。

那么此时,可以如此

final class Account{

  ……

}

猜你喜欢

转载自www.cnblogs.com/perfumeBear/p/11814125.html
今日推荐