原文: https://javatutorial.net/java-thread-synchronization

本文讨论了 Java 中线程同步的重要性以及如何在程序中实现线程同步。

当您的程序有多个线程时,当不同的线程尝试访问相同的资源或同时执行相同的操作时,可能会出现问题。 这种情况会导致错误和并发问题。 让我们以一个示例为例,您的程序中有两个线程试图读取/写入文本文件。 当两个线程都在写入文件时,一个线程的数据可能会被另一个线程覆盖,并且在读取文件时会发现相同的问题。

因此,线程同步是必需的,以便多个线程在任何给定时间点一次访问一个线程。

为什么要使用线程同步?

  • 防止线程干扰
  • 避免一致性问题和并发问题
  • 防止数据丢失

线程同步有互斥和线程间通信两种类型。

  • 互斥
  • 同步方法。
  • 同步块。
  • 静态同步。
  • 合作(Java 中的线程间通信)

同步块

同步块用于线程同步。

synchronized关键字用于标识 Java 中的同步块,并基于某些对象进行同步。 其背后的主要概念是,在同一对象上同步的所有同步块一次只能在其中一个线程内执行,这会阻止多个线程同时运行和执行。 尝试进入同步块的所有其他线程将被阻止,直到同步块内的线程退出该块为止。

共享资源保留在此同步块内,以便在给定时间点只有一个线程可以访问特定资源。

同步块的语法如下所示:

  1. synchronized(referencetoobject) {
  2. // Shared variables and other shared resources are placed here
  3. }

监视器

在尝试理解和实现 Java 同步时,监视器的概念很重要。

  • Java 中的每个对象都与一个监视器关联
  • 线程可以锁定或解锁监视器
  • 在给定时间,只有一个线程可以拥有监视器。
  • 一次只能有一个线程可以锁定监视器

线程同步示例

  1. //We write a program with a simple counter execution.
  2. class Countings {
  3. public void printing() {
  4. try {
  5. for(int i = 5; i > 0; i--) {
  6. System.out.println( i );
  7. }
  8. } catch (Exception e) {
  9. System.out.println("Thread interrupted.");
  10. }
  11. }
  12. }
  13. class Threadings extends Thread {
  14. private Thread thrd;
  15. private String thrdName;
  16. Countings gg;
  17. Threadings( String name, Countings abc) {
  18. thrdName = name;
  19. gg = abc;
  20. }
  21. public void run() {
  22. gg.printing();
  23. System.out.println("Thread " + thrdName + " exiting.");
  24. }
  25. public void start () {
  26. System.out.println("Starting " + thrdName );
  27. if (thrd == null) {
  28. thrd = new Thread (this, thrdName);
  29. thrd.start ();
  30. }
  31. }
  32. }
  33. public class Tests {
  34. public static void main(String args[]) {
  35. Countings gg = new Countings();
  36. Countings T1 = new Countings ( "Thread - 1 ", gg );
  37. Countings T2 = new Countings ( "Thread - 2 ", gg );
  38. T1.start();
  39. T2.start();
  40. // threads take some time to end
  41. try {
  42. T1.join();
  43. T2.join();
  44. } catch ( Exception e) {
  45. System.out.println("Interrupted");
  46. }
  47. }
  48. }

输出:(每次运行都会产生不同的结果)

  1. Starting Thread - 1
  2. Starting Thread - 2
  3. 5
  4. 4
  5. 3
  6. 5
  7. 2
  8. 1
  9. 4
  10. Thread Thread - 1 exiting.
  11. 3
  12. 2
  13. 1
  14. Thread Thread - 2 exiting.

相同的示例,但是这次具有线程同步:

  1. class Countings {
  2. public void printings() {
  3. try {
  4. for(int i = 5; i > 0; i--) {
  5. System.out.println( i );
  6. }
  7. } catch (Exception e) {
  8. System.out.println("Thread interrupted.");
  9. }
  10. }
  11. }
  12. class Threadings extends Thread {
  13. private Thread t;
  14. private String thrdName;
  15. Countings gg;
  16. Threadings( String name, Countings abc) {
  17. thrdName = name;
  18. gg = abc;
  19. }
  20. public void run() {
  21. synchronized(gg) {
  22. gg.printings();
  23. }
  24. System.out.println("Thread " + threadName + " exiting.");
  25. }
  26. public void start () {
  27. System.out.println("Starting " + thrdName );
  28. if (thrd == null) {
  29. thrd = new Thread (this, thrdName);
  30. thrd.start ();
  31. }
  32. }
  33. }
  34. public class Testings{
  35. public static void main(String args[]) {
  36. Countings gg = new Countings();
  37. Threadings T1 = new Threadings ( "Thread - 1 ", gg );
  38. Threadings T2 = new Threadings ( "Thread - 2 ", gg );
  39. T1.start();
  40. T2.start();
  41. // wait for threads to end
  42. try {
  43. T1.join();
  44. T2.join();
  45. } catch ( Exception e) {
  46. System.out.println("Interrupted");
  47. }
  48. }
  49. }

输出:(我们看到的输出是同步的,并且每次执行程序都是相同的)

  1. Starting Thread - 1
  2. Starting Thread - 2
  3. 5
  4. 4
  5. 3
  6. 2
  7. 1
  8. Thread Thread - 1 exiting.
  9. 5
  10. 4
  11. 3
  12. 2
  13. 1
  14. Thread Thread - 2 exiting.