この記事では、主に Java マルチスレッドの同期のいくつかの方法に関する関連情報を紹介します。必要な方は、
Java マルチスレッドのいくつかの同期方法
を参照してください。
2. スレッド同期が必要な理由
3. 同期されていない場合のコード
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. 同期を使用する場合のコード
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これはすぐに理解できるように感じます。
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 とほぼ同じです。
注: 同期はオーバーヘッドの高い操作であるため、同期されたコンテンツは最小限にする必要があります。通常、メソッド全体を同期する必要はなく、同期されたコード ブロックを使用してキー コードを同期するだけです。
a. volatile キーワードは、ドメイン変数にアクセスするためのロックフリーのメカニズムを提供します b. volatile を使用してドメインを変更することは、仮想マシンにc. ドメインが他のスレッドで更新される可能性があるため、フィールドが使用されるたびに、レジスタ内の値を使用する代わりに再計算されます。また、d.volatile はアトミックな操作を提供せず、最終型変数を装飾するために使用することもできません。
余额不足 账户余额:0 1441791806699存进:100 账户余额:100 1441791806700取出:100 账户余额:0 1441791807699存进:100 账户余额:100
どのように機能しますか?
またわかりにくいですか?どうしてこれなの?これは、volatile ではアトミックな操作が保証できないため、volatile は synchronized を置き換えることができないためです。さらに、volatile はコンパイラーによるコードの最適化を妨げるため、使用できない場合は適用しないでください。その原理は、スレッドが volatile で変更された変数にアクセスするたびに、キャッシュから変数を読み取るのではなくメモリから変数を読み取るため、各スレッドがアクセスする変数値は同じになるということです。これにより同期が確実に行われます。
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) ローカル変数を使用して実現します。スレッド同期
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 サイトの他の関連記事を参照してください。