Java中的静态同步

时间:2020-01-09 10:35:06  来源:igfitidea点击:

在Java中的同步中,已经讨论过Java中的每个对象都具有与其关联的单个锁(也称为监视器)。当线程进入同步方法或者同步块时,它将获取该锁。尝试执行相同代码的所有其他线程(在同步方法或者同步块中)必须等待第一个线程完成并释放锁。

但是,如果我们有多个同一个对象,那该怎么办?在这种情况下,两个单独的线程可以获取这两个对象的锁,并同时使用这些单独的锁输入同步方法或者同步块。如果我们不希望发生这种情况,则需要使用Java进行静态同步。

静态同步

我们必须知道,在Java中标记为静态的变量或者方法属于该类型,而不属于对象,并且此类变量或者方法将在类的实例之间共享。同样,Java中的静态同步也处于类级别而不是实例级别。

静态同步Java示例

让我们尝试通过一个示例来阐明Java中的静态同步。

在这里,我们有一个包含两个方法的类,在一个方法中,有一个for循环从1到5运行,并显示了这些值,在另一个方法中,有5个循环从5到1运行并显示了值。

创建了此类ctr1和ctr2的两个对象。还创建了四个线程,其中两个共享ctr1对象,而另外两个共享ctr2对象。我们所需要的是线程的执行方式应使循环中的值以1,2,3,4,5和5,4,3,2,1的顺序显示。为此,我们还同步了方法increment()和decrement()。

// Class whose object will be shared
class Counter{
  public synchronized void increment(){		
    for(int i = 1; i <= 5 ; i++){
      System.out.println(Thread.currentThread().getName() + " i - " + i);
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    } 
  }
	
  public synchronized void decrement(){		
    for(int i = 5; i > 0 ; i--){
      System.out.println(Thread.currentThread().getName() + " i - " + i);		   
    } 		
  }
}

class MyIncrementRunnable implements Runnable{
  Counter ctr;
  MyIncrementRunnable(Counter ctr){
    this.ctr = ctr;
    Thread t = new Thread(this);
    t.start();
  }
  @Override
  public void run() {
    ctr.increment();
  }
}

class MyDecrementRunnable implements Runnable{
  Counter ctr;
  MyDecrementRunnable(Counter ctr){
    this.ctr = ctr;
    Thread t = new Thread(this);
    t.start();
  }
  @Override
  public void run() {
    ctr.decrement();
  }
}

public class SynchronizedDemo {
  public static void main(String[] args) throws InterruptedException {
    // Two objects shared among threads
    Counter ctr1 = new Counter();
    Counter ctr2 = new Counter();
    
    // Thread 0 and Thread 1 for increment and decrement with same object
    MyIncrementRunnable mi1 = new MyIncrementRunnable(ctr1);
    MyDecrementRunnable md1 = new MyDecrementRunnable(ctr1);
    
    // Thread 2 and Thread 3 for increment and decrement with same object
    MyIncrementRunnable mi2 = new MyIncrementRunnable(ctr2);
    MyDecrementRunnable md2 = new MyDecrementRunnable(ctr2);
  }
}

输出:

Thread-0 i - 1
Thread-3 i - 5
Thread-3 i - 4
Thread-3 i - 3
Thread-3 i - 2
Thread-3 i - 1
Thread-2 i - 1
Thread-0 i - 2
Thread-2 i - 2
Thread-0 i - 3
Thread-2 i - 3
Thread-0 i - 4
Thread-2 i - 4
Thread-0 i - 5
Thread-2 i - 5
Thread-1 i - 5
Thread-1 i - 4
Thread-1 i - 3
Thread-1 i - 2
Thread-1 i - 1

从输出中可以看到,由于线程0和线程1在同一个对象ctr1上同步,因此这两个线程不会交织。在相同的对象ctr2上以相同的方式同步线程2和线程3,因此这两个线程不会交织。但是,即使线程0获得了锁,线程2仍可以进入同步方法,因为它们都有自己的独立对象锁。

使用静态同步

要解决此问题,我们需要使用静态同步在类级别而不是在实例级别进行同步。使用Java中的静态同步,线程获取与该类关联的Class对象的固有锁定。

在上面的示例中,我们只需要使方法增量()和减量()也成为静态方法即可。

// Class whose object will be shared
class Counter{
  public static synchronized void increment(){		
    for(int i = 1; i <= 5 ; i++){
      System.out.println(Thread.currentThread().getName() + " i - " + i);
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    } 
  }
	
  public static synchronized void decrement(){		
    for(int i = 5; i > 0 ; i--){
      System.out.println(Thread.currentThread().getName() + " i - " + i);		   
    } 		
  }
}

输出:

Thread-0 i - 1
Thread-0 i - 2
Thread-0 i - 3
Thread-0 i - 4
Thread-0 i - 5
Thread-2 i - 1
Thread-2 i - 2
Thread-2 i - 3
Thread-2 i - 4
Thread-2 i - 5
Thread-3 i - 5
Thread-3 i - 4
Thread-3 i - 3
Thread-3 i - 2
Thread-3 i - 1
Thread-1 i - 5
Thread-1 i - 4
Thread-1 i - 3
Thread-1 i - 2
Thread-1 i – 1

Java中的静态同步块

我们还可以在Java中使用静态同步块,而不是使用对象引用,而必须同步类对象本身。

Java中静态同步块的一般形式

Synchronized(CLASS_NAME.class)

范例程式码

在上面的示例中,我们可以将方法增量()和减量()更改为具有带有类引用的同步块。

// Class whose object will be shared
class Counter{
  public void increment(){
    // synchronized block with class reference
    synchronized(Counter.class){
      for(int i = 1; i <= 5 ; i++){
        System.out.println(Thread.currentThread().getName() + " i - " + i);
        try {
          Thread.sleep(50);
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      } 
    }
  }
	
  public void decrement(){	
    synchronized(Counter.class){
      for(int i = 5; i > 0 ; i--){
        System.out.println(Thread.currentThread().getName() + " i - " + i);		   
      } 	
    }
  }
}