検索
ホームページJava&#&チュートリアルJava マルチスレッド同期のいくつかの方法の紹介

この記事では、主に Java マルチスレッドの同期のいくつかの方法に関する関連情報を紹介します。必要な方は、

Java マルチスレッドのいくつかの同期方法

を参照してください。

数日前の面接で、マスターからまたたくさんの基礎知識を学ばなければなりませんでした。早速、本題に入りましょう。

2. スレッド同期が必要な理由

複数のスレッドが変数またはオブジェクトに同時にアクセスしている場合、これらのスレッドで読み取り操作と書き込み操作の両方が行われると、変数の値またはオブジェクトの状態が変化し、プログラム例外が発生します。たとえば、銀行口座が 2 つのスレッドで同時に操作されている場合、1 つは 100 元を引き出し、もう 1 つは 100 元を入金します。アカウントのブロックが元々 0 であると仮定します。出金スレッドと入金スレッドが同時に発生した場合はどうなりますか?出金に失敗し、口座残高は 100 です。 出金は成功し、口座残高は 0 です。では、どちらでしょうか?わかりにくいですね。そこで、この問題を解決するのがマルチスレッド同期です。

3. 同期されていない場合のコード

Bank.java


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

SyncThreadTest.java


package threadTest; 
/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class SyncThreadTest { 

  public static void main(String args[]){ 
    final Bank bank=new Bank(); 

    Thread tadd=new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          } 
          bank.addMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 

        } 
      } 
    }); 

    Thread tsub = new Thread(new Runnable() { 

      @Override 
      public void run() { 
        // TODO Auto-generated method stub 
        while(true){ 
          bank.subMoney(100); 
          bank.lookMoney(); 
          System.out.println("\n"); 
          try { 
            Thread.sleep(1000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          }   
        } 
      } 
    }); 
    tsub.start(); 

    tadd.start(); 
  } 

}

コードは非常に簡単なので、説明は省略します。みたいなの?一部を切り取ってみましたが、とてもごちゃごちゃしていて理解できません。


余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441790503354存进:100 
账户余额:100 

1441790504354存进:100 
账户余额:100 

1441790504354取出:100 
账户余额:100 

1441790505355存进:100 
账户余额:100 

1441790505355取出:100 
账户余额:100

4. 同期を使用する場合のコード

(1) 同期方法:

synchronizedキーワードで変更したメソッドがあります。 Java のすべてのオブジェクトには組み込みロックがあるため、このキーワードを使用してメソッドが変更されると、組み込みロックによってメソッド全体が保護されます。このメソッドを呼び出す前に、組み込みロックを取得する必要があります。取得しないとブロックされます。

Bank.java を変更しました


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public synchronized void addMoney(int money){ 
    count +=money; 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public synchronized void subMoney(int money){ 
    if(count-money < 0){ 
      System.out.println("余额不足"); 
      return; 
    } 
    count -=money; 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

実行結果を見てください:


余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441790837380存进:100 
账户余额:100 

1441790838380取出:100 
账户余额:0 
1441790838380存进:100 
账户余额:100 

1441790839381取出:100 
账户余额:0

これはすぐに理解できるように感じます。


注: synchronized キーワードは静的メソッドを変更することもできます。このときに静的メソッドが呼び出される場合、クラス全体がロックされます

(2) 同期されたコード ブロック

つまり、同期されたコード ブロックによって変更されるステートメント ブロックです。キーワード。このキーワードによって変更されたステートメント ブロックは、同期を実現するために組み込みロックとともに自動的に追加されます。

Bank.java コードは次のとおりです。


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count =0;//账户余额 

  //存钱 
  public  void addMoney(int money){ 

    synchronized (this) { 
      count +=money; 
    } 
    System.out.println(System.currentTimeMillis()+"存进:"+money); 
  } 

  //取钱 
  public  void subMoney(int money){ 

    synchronized (this) { 
      if(count-money < 0){ 
        System.out.println("余额不足"); 
        return; 
      } 
      count -=money; 
    } 
    System.out.println(+System.currentTimeMillis()+"取出:"+money); 
  } 

  //查询 
  public void lookMoney(){ 
    System.out.println("账户余额:"+count); 
  } 
}

実行結果は次のとおりです。効果は方法 1 とほぼ同じです。

注: 同期はオーバーヘッドの高い操作であるため、同期されたコンテンツは最小限にする必要があります。通常、メソッド全体を同期する必要はなく、同期されたコード ブロックを使用してキー コードを同期するだけです。

(3) 特殊なドメイン変数 (Volatile) を使用してスレッド同期を実現します


a. volatile キーワードは、ドメイン変数にアクセスするためのロックフリーのメカニズムを提供します b. volatile を使用してドメインを変更することは、仮想マシンにc. ドメインが他のスレッドで更新される可能性があるため、フィールドが使用されるたびに、レジスタ内の値を使用する代わりに再計算されます。また、d.volatile はアトミックな操作を提供せず、最終型変数を装飾するために使用することもできません。

Bank.java コードは次のとおりです:

余额不足 
账户余额:0 

1441791806699存进:100 
账户余额:100 

1441791806700取出:100 
账户余额:0 

1441791807699存进:100 
账户余额:100

どのように機能しますか?

rreee

またわかりにくいですか?どうしてこれなの?これは、volatile ではアトミックな操作が保証できないため、volatile は synchronized を置き換えることができないためです。さらに、volatile はコンパイラーによるコードの最適化を妨げるため、使用できない場合は適用しないでください。その原理は、スレッドが volatile で変更された変数にアクセスするたびに、キャッシュから変数を読み取るのではなくメモリから変数を読み取るため、各スレッドがアクセスする変数値は同じになるということです。これにより同期が確実に行われます。

(4) 再入ロックを使用してスレッド同期を実現する

同期をサポートするために、新しい java.util.concurrent パッケージが JavaSE5.0 に追加されました。 ReentrantLock クラスは、Lock インターフェイスを実装する再入可能な相互排他ロックであり、同期されたメソッドとブロックを使用する場合と同じ基本的な動作とセマンティクスを持ち、その機能を拡張します。 ReentrantLock クラスの一般的に使用されるメソッドは次のとおりです。 ReentrantLock(): ReentrantLock インスタンスの作成 lock(): ロックの取得unlock(): ロックの解放 注: ReentrantLock() には公平なロックを作成できるコンストラクターもありますが、プログラムを大幅に削減する可能性があります。 動作効率を高めるため、次のように Bank.java コードを変更することはお勧めできません:


package threadTest; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private volatile int count = 0;// 账户余额 

  // 存钱 
  public void addMoney(int money) { 

    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 
  } 

  // 取钱 
  public void subMoney(int money) { 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

動作効果はどうですか?

余额不足 
账户余额:0 

余额不足 
账户余额:100 

1441792010959存进:100 
账户余额:100 

1441792011960取出:100 
账户余额:0 

1441792011961存进:100 
账户余额:100

効果は最初の 2 つの方法と似ています。

synchronized キーワードがユーザーのニーズを満たすことができる場合は、コードを簡素化できる synchronized を使用してください。より高度な機能が必要な場合は、ReentrantLock クラスを使用してください。このとき、ロックの解放が間に合うように注意してください。そうしないと、通常は、finally コードでロックが解放されます

(5) ローカル変数を使用して実現します。スレッド同期

Bank.java コードは次のとおりです:


package threadTest; 

import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 

/** 
 * @author ww 
 * 
 */ 
public class Bank { 

  private int count = 0;// 账户余额 

  //需要声明这个锁 
  private Lock lock = new ReentrantLock(); 

  // 存钱 
  public void addMoney(int money) { 
    lock.lock();//上锁 
    try{ 
    count += money; 
    System.out.println(System.currentTimeMillis() + "存进:" + money); 

    }finally{ 
      lock.unlock();//解锁 
    } 
  } 

  // 取钱 
  public void subMoney(int money) { 
    lock.lock(); 
    try{ 

    if (count - money < 0) { 
      System.out.println("余额不足"); 
      return; 
    } 
    count -= money; 
    System.out.println(+System.currentTimeMillis() + "取出:" + money); 
    }finally{ 
      lock.unlock(); 
    } 
  } 

  // 查询 
  public void lookMoney() { 
    System.out.println("账户余额:" + count); 
  } 
}

操作の効果:

余额不足 
账户余额:0 

余额不足 
账户余额:0 

1441792891934存进:100 
账户余额:100 

1441792892935存进:100 
账户余额:200 

1441792892954取出:100 
账户余额:100

操作の効果を見た後、最初はなぜ入金のみが許可され、引き出しは許可されないのかと混乱しました。 ThreadLocal の原理を見てみましょう:

ThreadLocal を使用して変数を管理する場合、変数を使用する各スレッドは変数のコピーを取得します。コピーは相互に独立しているため、各スレッドは他のスレッドに影響を与えることなく、変数の独自のコピーを自由に変更できます。これで、各スレッドがコピーを実行していることがわかりました。これは、お金の入金と出金が同じナレッジ名を持つ 2 つのアカウントであることを意味します。したがって、上記の効果が発生します。

ThreadLocal と同期メカニズム

a.ThreadLocal と同期メカニズムは両方とも、マルチスレッドでの同じ変数のアクセス競合の問題を解決するためのものです。 b. 前者は、「空間を時間に交換する」方法を採用しています。後者は「時間を空間と交換する」方法を採用しています

以上がJava マルチスレッド同期のいくつかの方法の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター