Synchronized关键字

Synchronized关键字说明

  Java中的同步代码块用Synchronized关键字来标记,而该关键字依赖于JVM来实现,因而我们不用自己去考虑如何去加锁与解锁,加锁与解锁操作由JVM本身来维护,这就简化了我们对Synchronized关键字的使用难度。

  在Java中的一个同步代码块有时是同步在不同的对象上的。在相同的对象上,在同一时间内所有的同步代码块中只有一个线程在执行。所有其它的线程都在试图进入同步代码块时被阻塞,直到同步代码快中的线程退出该块代码为止。

  Synchronized属于不可中断锁,它的使用场景是那些适合竞争不太激烈的地方,可读性好。

  synchronized关键字可以用来修饰如下四种不同类型的代码块:

  • 实例方法
  • 静态方法
  • 实例方法中的代码块
  • 静态方法中的代码块

  这些块在不同的对象上同步。您需要哪种同步块取决于具体的情况。

实例方法

  实例方法如下:

public synchronized void add(int value){
    this.count += value;
}

  注意方法声明中使用了synchronized关键字。这告诉Java方法是同步的。

  Java中的同步实例方法在拥有该方法的实例(对象)上同步。因此,每个实例的同步方法在不同的对象上同步:拥有实例。在同步实例方法中只能执行一个线程。如果存在多个实例,则每次可以在每个实例的同步实例方法中执行一个线程。每个实例一个线程。

静态方法

  静态方法被标记为synchronized,就像使用synchronized关键字的实例方法一样。下面是一个Java同步静态方法的例子:

public static synchronized void add(int value){
    count += value;
}

  这里的synchronized关键字还告诉Java方法是同步的。

  同步静态方法在同步静态方法所属的类的类对象上同步。由于每个类在Java VM中只存在一个类对象,所以在同一个类的静态同步方法中只能执行一个线程。

  如果静态同步方法位于不同的类中,则可以在每个类的静态同步方法中执行一个线程。每个类一个线程,不管它调用哪个静态同步方法。

实例方法中的代码块

  您不必同步整个方法。有时,最好只同步方法的一部分。方法内部的Java同步块使这成为可能。

  这是一个非同步Java方法中的同步Java代码块:

public void add(int value){

  synchronized(this){
     this.count += value;   
  }
}

  本例使用Java同步块构造函数将代码块标记为同步。这段代码现在将像同步方法一样执行。

  注意Java同步代码块构造函数如何接受括号中的对象。在使用“this”的示例中,它是调用add方法的实例。同步结构在括号中获取的对象称为监视器对象。该代码被认为是在监视器对象上同步的。同步实例方法使用它所属的对象作为监视器对象。

  在同一监视器对象上同步的Java代码块中只能执行一个线程。

  下面两个示例都是在调用它们的实例时同步的。因此,它们在同步方面是等价的:

public class MyClass {

  public synchronized void log1(String msg1, String msg2){
     log.writeln(msg1);
     log.writeln(msg2);
  }


  public void log2(String msg1, String msg2){
     synchronized(this){
        log.writeln(msg1);
        log.writeln(msg2);
     }
  }
}

  因此,在本例中,只有一个线程可以在两个同步块中执行。

  如果在不同的对象上同步了第二个同步块,那么每次就可以在每个方法中执行一个线程。

静态方法中的代码块

  下面是与静态方法相同的两个示例。这些方法在方法所属类的类对象上同步:

public class MyClass {

  public static synchronized void log1(String msg1, String msg2){
     log.writeln(msg1);
     log.writeln(msg2);
  }


  public static void log2(String msg1, String msg2){
     synchronized(MyClass.class){
        log.writeln(msg1);
        log.writeln(msg2);  
     }
  }
}

  这两个方法中的任何一个都只能同时执行一个线程。

  是否将第二个同步块同步到另一个不同于MyClass.class的对象上,然后在每个方法中可以同时执行一个线程。

Synchronized用例

  下面的示例启动了两个线程,并让它们在同一个Counter实例上调用add方法。一次只能调用同一个实例上的add方法,因为该方法在它所属的实例上是同步的。

  Counter

public class Counter{
   
   long count = 0;
  
   public synchronized void add(long value){
     this.count += value;
   }
}

  CounterThread

public class CounterThread extends Thread{

   protected Counter counter = null;

   public CounterThread(Counter counter){
      this.counter = counter;
   }

   public void run() {
for(int i=0; i<10; i++){
         counter.add(i);
      }
   }
}

  Example

public class Example {

  public static void main(String[] args){
    Counter counter = new Counter();
    Thread  threadA = new CounterThread(counter);
    Thread  threadB = new CounterThread(counter);

    threadA.start();
    threadB.start(); 
  }
}

  上面的示例中一共创建了两个线程。相同的Counter被装载到两个不同的CounterThread实例构造函数中。Counter.add()方法通过synchronized关键字在实例上同步,因为add方法是一个实例方法,它通过synchronized关键字来修饰,因此,每次只有一个线程可以调用add()方法。另一个线程将等到第一个线程离开add()方法后才能执行该方法本身。

  如果这两个线程引用了两个单独的Counter实例,那么同时调用add()方法就不会有问题。调用对象将是不同的对象,因此调用的方法也将在不同的对象(拥有该方法的对象)上同步。因此调用不会阻塞。此时这个过程这看起来是这样的:

public class Example {

  public static void main(String[] args){
    Counter counterA = new Counter();
    Counter counterB = new Counter();
    Thread  threadA = new CounterThread(counterA);
    Thread  threadB = new CounterThread(counterB);

    threadA.start();
    threadB.start(); 
  }
}

  注意,threadAthreadB这两个线程不再引用同一个Counter实例。counterAcounterBadd方法在各自拥有的两个实例上同步。因此,调用counterA上的add()不会阻塞对counterBadd()的调用。

参考文章:Java Synchronized Blocks

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值