ここにまとめたスレッドの基本、マルチスレッドプログラミングのために、この最も重要なの一部は、の最も厄介な部分- 同期が要約されるように。
スレッドを作成することは困難ではない、難易度は複数のスレッドが良好な協力関係を実行できるようにする方法を、マルチスレッド処理を必要とするもののほとんどは、ここにデータの共有に主に関連した、完全に独立していないが、これは、スレッド同期の概要であります地元の欠陥のある場合は、コメントで指摘してお待ちしております。
なぜ同期すべきです
乱数がNUM1、追加NUM2から減算されるたびに - 簡単な例を見てみましょう、そんなことをするために10個のスレッドを持つ今NUM1、NUM2 2つの数字が、あります。
public class Demo1 {
public static void main(String[] args) {
Bank bank = new Bank();
//创建10个线程,不停的将一个账号资金转移到另一个账号上
for (int i = 0; i < 100; i++) {
new Thread(() -> {
while (true) {
int account1 = ((Double) Math.floor(Math.random() * 10)).intValue();
int account2 = ((Double) Math.floor(Math.random() * 10)).intValue();
int num = ((Long) Math.round(Math.random() * 100)).intValue();
bank.transfer(account1, account2, num);
try {
Thread.sleep(((Double) (Math.random() * 10)).intValue());
} catch (Exception e) {
}
}
}).start();
}
}
}
class Bank {
/**
* 10个资金账户
*/
public int[] accounts = new int[10];
public Bank() {
Arrays.fill(accounts, 1000);
}
public void transfer(int from, int to, int num) {
accounts[from] -= num;
accounts[to] += num;
//计算和
int sum = 0;
for (int j = 0; j < 10; j++) {
sum += accounts[j];
}
System.out.println(sum);
}
}
复制代码
通常の状況下では、何があっ資本勘定の時間と10000しかし、すべき全て本当にそう?10000に等しいプログラムを実行していないことは、一定期間の後、大きくなったり、小さくなっていることがわかります。
競争
アカウント情報を更新するために、同時に複数のプログラムでは、上記のコードなので、競争がありました。2つのスレッドが同時に実行するコード以下とします
accounts[account1] -= num;
复制代码
コードは、次の3つの手順に加工することができる、アトミックではありません。
- アカウント[account1]レジスタにロードされます
- NUMの値を減らします
- 結果はアカウントに書き戻される[account1]
スレッドAがスレッドを実行する権利を奪わ第3工程を、実行した場合(マルチコア問題が存在することになるように)単一コアの場合には、ここで説明した問題は、同時に二つのスレッドを実行していない単一のコアは、実行完了Bを開始しますプロセス全体は、次いで、エラーとなった第3工程を、スレッド実行を継続、スレッド、スレッドBの結果をカバーした結果は、総量はもはや正確でありません。下図のように:
どのように同期します
ロックオブジェクト
混乱を同時データリードを防ぐために、Java言語にはsynchronizedキーワードを提供し、そしてときReentrantLockのJava SEの5のクラスに参加しました。synchronizedキーワードが自動的にロックおよび関連条件を提供し、バックは言います。ReentrantLockのは、実質的に、以下のものを使用して:
myLock.lock()//myLock是一个ReetrantLock对象示例
try{
//要保护的代码块
}finally{
//一定要在finally中释放锁
myLock.unlock();
}
复制代码
上記構成のいずれかのスレッドがロックを取得するためにロックメソッドを呼び出したら、一つだけのスレッドが、クリティカルセクションに入ることを保証し、他のすべてのスレッドは、ロック解除方法まで、ロック方式、呼び出し元のスレッドにブロックされます。
転送方法禁止クラスロックコードは次の通りであります:
class Bank {
/**
* 10个资金账户
*/
public int[] accounts = new int[10];
private ReentrantLock lock = new ReentrantLock();
public Bank() {
Arrays.fill(accounts, 1000);
}
public void transfer(int from, int to, int num) {
try {
lock.lock();
accounts[from] -= num;
accounts[to] += num;
//计算和
int sum = 0;
for (int j = 0; j < 10; j++) {
sum += accounts[j];
}
System.out.println(sum);
} finally {
lock.unlock();
}
}
}
复制代码
ロックした後、関係なく、同時に実行する方法を多くのスレッド、データが混乱を招くません。
ロックは、再入可能ロックを保持し、スレッドがすでに開催されたロックを獲得繰り返されている場合があります。ネストされたコールのロック方法を追跡するロック保持カウント(ホールド数)があります。たびにロックカウント+ 1、ロックが夜12時に解放されたときに1つのカウント-1ロックを解除。
ブール・パラメータ地殻ベルトによるロック公平性ポリシーを持つことができます- new ReentrantLock(true)
。フェアロックの好み最長待機しているスレッド。しかし、大幅にパフォーマンスが低下、それでも公正なロックで、スレッドスケジューラが公平である保証はありません。
条件オブジェクト
通常、私たちは一つのスレッドがロックを取得すると、取得されたロックを管理するための条件オブジェクトを必要とする次の実行に継続するために、一定の条件を満たしている必要性を見つけることが、有用な作業スレッドをしない、このような問題に遭遇します。
今の制限を転送するプラスを検討し、十分なだけの資金がロールアウトなどのアカウントにアカウントが、また、彼らは否定することはできませんことを。次のコードが実行可能ではないことに注意してください。
if(bank.accounts[from]>=num){
bank.transfer(from,to,num);
}
复制代码
裁判官が成功した場合、マルチスレッドの可能性があるので、データはちょうど別のスレッドが変更されています。
あなたは、ターゲットの条件を決定することにより達成することができます。
class Bank {
/**
* 10个资金账户
*/
public int[] accounts = new int[10];
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public Bank() {
Arrays.fill(accounts, 1000);
}
public void transfer(int from, int to, int num) {
try {
lock.lock();
while (accounts[from] < num) {
//进入阻塞状态
condition.await();
}
accounts[from] -= num;
accounts[to] += num;
//计算和
int sum = 0;
for (int j = 0; j < 10; j++) {
sum += accounts[j];
}
System.out.println(sum);
//通知解除阻塞
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
复制代码
whileループは、条件が満たされない場合、か否かを判断するには、呼び出しawait
ロックを放棄しながら、遮断状態にする方法を。これは、他のスレッドに判定条件を満足することが判明するも、資金を転送するためにアカウントを与える機会を可能にします。
スレッドはジョブの転送を終了すると、あなたが呼び出す必要がありsignalAll
、すべての接点ブロックのスレッドがブロックされるように裁判官が条件を満たしている可能性があるため、あなたが転送動作を継続することができ、方法を。
注意:通話はsignalAll
すぐに裁判官が続いている間、彼らは、競争を通じてロック・スレッドを取得できるように、ちょうど、遮断状態で連絡し、待機中のスレッドをアクティブにしません。
方法もありますsignal
ランダムブロック解除のスレッドが。ここでは、デッドロックにつながる可能性があります。
synchronizedキーワード
ロックの1と条件に開発者のための強力な同期制御を提供します。しかし、ほとんどのケースでは、このような複雑な制御を必要としません。Javaのバージョン1.0を起動し、Javaの各オブジェクトは、内部ロックを持っています。この方法の場合はsynchronized
宣言、そのオブジェクトのロックは、全体のプロセスがロック内部の自動取得メソッドの呼び出し、自動的にメソッドの最後にリリースされた内部ロックで保護されます。
同じとReentrantLockのロック次のように、内部ロック待ち/のnotifyAll /方法を通知するがあり、対応があります。
- 待つことに対応待ちます
- notifyAllはsignalAllに対応します
- 対応する信号を通知する独特のメソッド名で、これらのメソッドは、オブジェクトクラスの最終的な方法である待っていない競合するために、あるので、
ReentrantLock
クラスメソッドの名前を変更する必要があります。
次のクラスを達成するために禁止と同期:
class Bank {
/**
* 10个资金账户
*/
public int[] accounts = new int[10];
private ReentrantLock lock = new ReentrantLock();
// private Condition condition = lock.newCondition();
public Bank() {
Arrays.fill(accounts, 1000);
}
synchronized public void transfer(int from, int to, int num) {
try {
// lock.lock();
while (accounts[from] < num) {
//进入阻塞状态
// condition.await();
this.wait();
}
accounts[from] -= num;
accounts[to] += num;
//计算和
int sum = 0;
for (int j = 0; j < 10; j++) {
sum += accounts[j];
}
System.out.println(sum);
//通知解除阻塞
// condition.signalAll();
this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
// finally {
// lock.unlock();
// }
}
}
复制代码
同期として静的メソッドは、このメソッド、クラスに対応する内部ロック・クラス・オブジェクトへのアクセスを呼び出して、宣言することができます。
コードを使用する方法
-
最高でもないロック/条件は、synchronizedキーワードを使用しないで、主にjava.util.concurrentのパッケージには、データ同期ケースを完了するために、クラスで使用することができ、パッケージタイプは、スレッドセーフです。これは、次のいずれかを述べます。
-
あなたは、同期を使用することができた場合は、両方のエラーの可能性を低減、コードの量を減らすために、それを使用してみてください。
-
上記の問題が解決しない場合は、それが唯一のロック/条件を使用します。
:使用されているすべてのコードBenpian GitHubの
元の記事に掲載さ:www.tapme.top/blog/detail ...
マイクロチャネル公共番号を懸念スキャンコード:FleyX研究ノート、より乾燥品について