Java 简明教程

Java - Block Synchronization

当我们在 Java 程序内启动 two or more threads时,可能会出现多个线程尝试访问同一资源的情况,最终它们可能会由于并发问题而产生意外结果。例如,如果多个线程尝试在同一文件中写入,则它们可能会破坏数据,因为其中一个线程可以覆盖数据,或者在其中一个线程打开同一文件的同时,另一个线程可能关闭同一文件。

When we start two or more threads within a Java program, there may be a situation when multiple threads try to access the same resource and finally they can produce unforeseen result due to concurrency issues. For example, if multiple threads try to write within a same file then they may corrupt the data because one of the threads can override data or while one thread is opening the same file at the same time another thread might be closing the same file.

因此,需要同步多个线程的操作并确保在给定的时间点只有单个线程可以访问资源。这是使用监视器的概念来实现的。Java 中的每个对象都与一个监视器相关联,该监视器可以由线程锁定或解锁。一次只能有一个线程对监视器持有锁。

So there is a need to synchronize the action of multiple threads and make sure that only one thread can access the resource at a given point in time. This is implemented using a concept called monitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor.

Block Synchronization in Java

Java 编程语言提供了一种非常方便的方法来创建线程并 synchronizing使用同步块来完成它们的任务。您保留此块内的共享资源。

Java programming language provides a very handy way of creating threads and synchronizing their task by using synchronized blocks. You keep shared resources within this block.

Syntax

以下是同步语句的一般形式 −

Following is the general form of the synchronized statement −

synchronized(objectidentifier) {
   // Access shared variables and other shared resources
}

此处,objectidentifier 是对一个对象的引用,其锁与同步语句所表示的监视器相关联。现在,我们将看到两个示例,其中我们将使用两个不同的线程打印计数器。当线程不同步时,它们会打印不是按顺序的计数器值,但是当我们通过将代码放入 synchronized() 块内打印计数器时,则对于这两个线程都会非常顺序地打印计数器。

Here, the objectidentifier is a reference to an object whose lock associates with the monitor that the synchronized statement represents. Now we are going to see two examples, where we will print a counter using two different threads. When threads are not synchronized, they print counter value which is not in sequence, but when we print counter by putting inside synchronized() block, then it prints counter very much in sequence for both the threads.

Multithreading Example without Synchronization

这是一个简单的示例,它可能并不会按顺序打印 counter 的值,而且每次运行都会基于线程对 CPU 的可用性产生不同的结果。

Here is a simple example which may or may not print counter value in sequence and every time we run it, it produces a different result based on CPU availability to a thread.

Example

package com.tutorialspoint;

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i >  0; i--) {
            Thread.sleep(50);
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  printDemo;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      printDemo = pd;
   }

   public void run() {
      printDemo.printCount();
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {
   public static void main(String args[]) {

      PrintDemo printDemo = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo( "Thread - 1 ", printDemo );
      ThreadDemo t2 = new ThreadDemo( "Thread - 2 ", printDemo );

      t1.start();
      t2.start();

      // wait for threads to end
         try {
         t1.join();
         t2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Output

每次运行该程序都会产生不同的结果——

This produces a different result every time you run this program −

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   5
Counter   ---   4
Counter   ---   4
Counter   ---   3
Counter   ---   3
Counter   ---   2
Counter   ---   2
Counter   ---   1
Counter   ---   1
Thread Thread - 1  exiting.
Thread Thread - 2  exiting.

Multithreading Example with Synchronization at Block level

以下是按顺序打印计数器值的示例,每次运行都会产生相同的结果。我们在一个块上放置了 synchronized 关键字,以便在方法执行期间计数器递增代码现在按对象锁定。我们使用当前对象作为锁,将其作为参数传递给同步块。

Here is the same example which prints counter value in sequence and every time we run it, it produces the same result. We’ve put synchronized keyword over a block so that counter increment code is now locked as per the object during method execution. We’re using current object as lock which we’re passing in the synchronized block as parameter.

Example

package com.tutorialspoint;

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            Thread.sleep(50);
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo printDemo;

   ThreadDemo( String name, PrintDemo pd) {
      threadName = name;
      printDemo = pd;
   }

   public void run() {
      synchronized(printDemo) {
         printDemo.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo printDemo = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo( "Thread - 1 ", printDemo );
      ThreadDemo t2 = new ThreadDemo( "Thread - 2 ", printDemo );

      t1.start();
      t2.start();

      // wait for threads to end
      try {
         t1.join();
         t2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Output

每次运行此程序都会产生相同的结果 −

This produces the same result every time you run this program −

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Multithreading Example with Synchronization at Method level

这是一个在每次运行时都会按顺序打印 counter 值的示例,而且每次都产生相同的结果。这次,我们在一个方法上放了 synchronized 关键字,因此在方法执行期间,整个方法会按照对象锁定。

Here is the same example which prints counter value in sequence and every time we run it, it produces the same result. W’ve put synchronized keyword over a method this time so that complete method is locked as per the object during method execution.

Example

package com.tutorialspoint;

class PrintDemo extends Thread {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            Thread.sleep(50);
            System.out.println("Counter --- " + i );
         }
      } catch (Exception e) {
         System.out.println("Thread " + Thread.currentThread().getName()+" interrupted.");
      }
   }
   public synchronized void run() {
      printCount();
      System.out.println("Thread " + Thread.currentThread().getName() + " exiting.");
   }
}
public class TestThread {
   public static void main(String args[]) {
      PrintDemo printDemo = new PrintDemo();
      Thread t1 = new Thread(printDemo );
      Thread t2 = new Thread(printDemo );
      t1.start();
      t2.start();
      // wait for threads to end
      try {
         t1.join();
         t2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Output

每次运行此程序都会产生相同的结果 −

This produces the same result every time you run this program −

Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread-1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread-2 exiting.