ホームページ  >  記事  >  Java  >  Java の同時プログラミングの 3 つの要素について詳しく学ぶ

Java の同時プログラミングの 3 つの要素について詳しく学ぶ

WBOY
WBOY転載
2022-04-22 11:54:482718ブラウズ

この記事では、java に関する関連知識を提供します。主に、アトミック性、可視性、順序性、およびそれらの生成など、同時プログラミングの 3 つの要素に関連する問題を紹介します。その理由、定義、など、以下、皆さんのお役に立てれば幸いです。

Java の同時プログラミングの 3 つの要素について詳しく学ぶ

推奨学習: 「

Java ビデオ チュートリアル

1 Atomicity

1.1 アトミック性の定義

アトミック性とは、すべて実行され、実行プロセス中に他の操作によって中断されない 1 つ以上の操作、または単に実行されない操作を指します。モール。

1.2 アトミック性の問題の原因

スレッドの切り替えはアトミック性の問題の原因であり、スレッドの切り替えは CPU 使用率を改善するために行われます。

count を例に挙げると、少なくとも 3 つの CPU 命令が必要です:

    命令 1: まず、変数 count をメモリから CPU レジスタにロードする必要があります。
  • #命令 2: その後、レジスタで 1 つの操作を実行します;
  • 命令 3: 最後に、結果をメモリに書き込みます (キャッシュ メカニズムにより、メモリではなく CPU キャッシュに書き込まれる場合があります) )。
  • count=0 と仮定します。命令 1 の実行後にスレッド A がスレッドを切り替え、スレッド A とスレッド B が下図の順序に従って実行されると、両方のスレッドがCount =1 の操作が実行されましたが、結果は期待した 2 ではなく 1 でした。

1.3 アトミック操作

マルチスレッド環境では、Java は、基本データ型の変数と代入操作がアトミックであることのみを保証します (

Note : 32 ビット JDK 環境では、64 ビット データの読み取りは、long、double などのアトミック操作* ではありません

)1.4 アトミック性の問題を解決する方法

If共有変数への変更が相互に排他的であることを確認できれば、シングルコア CPU であってもマルチコア CPU であってもアトミック性が保証されます。ロックにより、synchronized や lock の使用などの原子性の問題を解決できます。

2 可視性

2.1 可視性の定義

可視性とは、複数のスレッドが共有変数を操作する場合を指します。ここで、1 つのスレッドの後で変数を変更すると、他のスレッドは変更の結果をすぐに確認できます。

2.2 可視性問題の原因

可視性問題の原因は、CPU キャッシュとメモリ間のデータ整合性です CPU キャッシュは、CPU の効率を向上させるためのものです。

2.3 可視性の問題の解決策

可視性の問題の原因は CPU キャッシュです。その場合は、CPU キャッシュを無効にできます。

volatile フィールドは CPU キャッシュを無効にし、可視性の問題を解決できます。
  • 同期とロックの両方で可視性を保証できます。
  • 2.4 可視性ルールとは

可視性ルールは Happens-Before ルールです。

Happens-Before ルール:

簡単に言えば:
  • 前の操作の結果は、後続の操作から参照できますHappens-Before は、コンパイラの最適化動作を制限します。コンパイラは最適化を許可されていますが、最適化後にコンパイラが Happens-Before ルールに従う必要があります。
  • 2.5 Happens-Before ルール

    プログラム シーケンス ルール
  • スレッド内では、プログラムの順序に従ってください。 、前の操作は後続の操作の前に発生します。
class Example {
  public void test() {
    int x = 42;   ①
    int y = 20;   ②
  }
 
}

①は②の前に起こります。

    volatile 変数のルール
  • volatile 変数への書き込み操作は、その後の volatile 変数への読み取り操作の前に行われます。

    推移的ルール
  • A が B より前に発生し、B が C より前に発生した場合、A は C より前に発生します。
class Example {
  int x = 0;
  volatile int y = 0;
  public void writer() {
    x = 42;      ①
    y = 1;       ②
  }
  public void reader() {
    if (y == 1) {  ③
      // 这里x会是多少呢?
    }
  }
}

① 発生 - ②の前に、ルール 1 を満たします - 連続ルール。
  • ② 発生 - ③の前に、ルール 2 - 揮発性変数ルールを満たす。
  • ① 発生 - ③の前に、ルール 3 を満たす - 推移的ルール。 y == 1 の場合、x = 42;
    モニター内のロックのルール
  • ロックのロック解除が発生する前このロックのその後のロックのために。

管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。

synchronized (this) { //此处自动加锁
  // x是共享变量,初始值=10
  if (this.x < 12) {
    this.x = 12; 
  }  
} //此处自动解锁

假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁);

线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x==12。

  • 线程 start() 规则

它是指主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。

  • 线程 join() 规则

它是指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。当然所谓的“看到”,指的是对共享变量的操作。

3 有序性

3.1 有序性的定义

有序性,即程序的执行顺序按照代码的先后顺序来执行。

3.2 有序性问题原因

编译器为了优化性能,有时候会改变程序中语句的先后顺序。

例如:“a=6;b=7;”编译器优化后可能变成“b=7;a=6;”,在这个例子中,编译器调整了语句的顺序,但是不影响程序的最终结果。

以双重检查代码为例:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {    ①
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();  ②
        }
    }
    return instance;
  }
}

上面的代码有问题,问题在 ② 操作上:经过优化后的执行路径是这样的:

  1. 分配一块内存 M;
  2. 将 M 的地址赋值给 instance 变量;
  3. 最后在内存 M 上初始化 Singleton 对象。

优化后会导致什么问题呢?我们假设线程 A 先执行 getInstance() 方法,当执行完 ① 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。

如何解决双重检查问题?变量用 volatile 来修饰,禁止指令重排序

public class Singleton {
  static volatile Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {    ①
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();  ②
        }
    }
    return instance;
  }
}

推荐学习:《java视频教程

以上がJava の同時プログラミングの 3 つの要素について詳しく学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。