二日前、オタクの時間を確認しJava
たブラシコンセプトに、時間の同時のコースを:ライブロックを。デッドロックが、ライブロックには見知らぬ人が最初に聞いたことはありません追加。
ライブロック、レッツ・レビューデッドロックを導入する前に、次の例では、転送サービス、マルチスレッド環境をシミュレートし、セキュリティの量を考慮するために、アカウントがロックされています。
public class Account {
public Account(int balance, String card) {
this.balance = balance;
this.card = card;
}
private int balance;
private String card;
public void addMoney(int amount) {
balance += amount;
}
// 省略 get set 方法
}
public class AccountDeadLock {
public static void transfer(Account from, Account to, int amount) throws InterruptedException {
// 模拟正常的前置业务
TimeUnit.SECONDS.sleep(1);
synchronized (from) {
System.out.println(Thread.currentThread().getName() + " lock from account " + from.getCard());
synchronized (to) {
System.out.println(Thread.currentThread().getName() + " lock to account " + to.getCard());
// 转出账号扣钱
from.addMoney(-amount);
// 转入账号加钱
to.addMoney(amount);
}
}
System.out.println("transfer success");
}
public static void main(String[] args) {
Account from = new Account(100, "6000001");
Account to = new Account(100, "6000002");
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 线程 1
threadPool.execute(() -> {
try {
transfer(from, to, 50);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 线程 2
threadPool.execute(() -> {
try {
transfer(to, from, 30);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
上記の例では、転写方法に2つのスレッドが、スレッド1を取得するアカウントとき6000001ロック、スレッド2ロックされたアカウント6000002ロック。
取得し、次いで場合、スレッド1希望6000002ロックがスレッド2に保持されているので、ロックは、スレッド1とスレッド状態にブロックされるBLOCKED。同様に、スレッド2は、同じ状態です。
pool-1-thread-1 lock from account 6000001
pool-1-thread-2 lock from account 6000002
ログインした後、あなたは待ちに入り、2つのスレッドが転送方法を始めるのを見ることができます。
synchronized
ロックが待って、ブロックされ得るわけではありません。この場合、我々は、使用することができますReentrantLock#tryLock(long timeout, TimeUnit unit)
変換します。tryLock
私たちはロックが返されます取得できる場合はtrue
、それがロックを取得できない場合は、以下の条件が満たされるまで、お待ちしております。
- タイムアウト期間内にロック、リターンを取得します
true
- 復帰にロックタイムアウトまでの時間を取得していません
false
- 割り込み、例外がスローされます
次のように変換コードの後にあります。
public class Account {
public Account(int balance, String card) {
this.balance = balance;
this.card = card;
}
private int balance;
private String card;
public void addMoney(int amount) {
balance += amount;
}
// 省略 get set 方法
}
public class AccountLiveLock {
public static void transfer(Account from, Account to, int amount) throws InterruptedException {
// 模拟正常的前置业务
TimeUnit.SECONDS.sleep(1);
// 保证转账一定成功
while (true) {
if (from.lock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock from account " + from.getCard());
if (to.lock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock to account " + to.getCard());
// 转出账号扣钱
from.addMoney(-amount);
// 转入账号加钱
to.addMoney(amount);
break;
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
}
System.out.println("transfer success");
}
public static void main(String[] args) {
Account from = new Account(100, "A");
Account to = new Account(100, "B");
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 线程 1
threadPool.execute(() -> {
try {
transfer(from, to, 50);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 线程 2
threadPool.execute(() -> {
try {
transfer(to, from, 30);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
用途上記のコードwhile(true)
は、ロックを取得する失敗は、それが成功するまで再試行保ちます。このメソッドを実行すると、ラッキーポイントは、1が成功し、不運をすることができ、それは次の通りになります。
pool-1-thread-1 lock from account 6000001
pool-1-thread-2 lock from account 6000002
pool-1-thread-2 lock from account 6000002
pool-1-thread-1 lock from account 6000001
pool-1-thread-1 lock from account 6000001
pool-1-thread-2 lock from account 6000002
transfer
メソッドが実行されているが、最終的な結果が成功しなかった、これはライブロックの一例です。
デッドロックが仮死に、スレッドがブロックされているのと同じようなプログラムのルックスを引き起こします。道路と同じように、男は、あなたが私を見つめ、私はお互いに道を譲るのを待って、あなたを見つめてる、そして最終的に誰が生活が困難になることができます。
ライブロックは、同じスレッドリピート同じ操作ではありませんが、それは失敗し、実行しました。また、上記のあなたは、右のステップ彼は、賢い左ステップ、この時間を例に取ると、彼らはに実行します。その後、連続サイクルは、ほとんどのか、誰が生活が困難になります。
デッドロックこの例の分析、二つのスレッドの取得のためには、お互いの手がロック必要につながる、矛盾をロックします。2つのスレッドが同じ順序でロックした場合は、必要な条件は、同じになり、デッドロックにバインドされません。
私たちは、次のようにコードが修正されるので、あなたがデッドロックを解決できることを、すべての時間は、アカウントに最初の大規模なロックを与えたサイズのカード番号、カード番号を注文します:
// 其他代码不变
public static void transfer(Account from, Account to, int amount) throws InterruptedException {
// 模拟正常的前置业务
TimeUnit.SECONDS.sleep(1);
Account maxAccount=from;
Account minAccount=to;
if(Long.parseLong(from.getCard())<Long.parseLong(to.getCard())){
maxAccount=to;
minAccount=from;
}
synchronized (maxAccount) {
System.out.println(Thread.currentThread().getName() + " lock account " + maxAccount.getCard());
synchronized (minAccount) {
System.out.println(Thread.currentThread().getName() + " lock account " + minAccount.getCard());
// 转出账号扣钱
from.addMoney(-amount);
// 转入账号加钱
to.addMoney(amount);
}
}
System.out.println("transfer success");
}
例えば、ライブロックのために、2つの問題があります。
まず、同時にロック、ロックの再試行時間を解放ほぼ二つのスレッドで、その結果、同じでタイムアウトロックをロックすると同時に、その後、無限ループに入ります。この問題を解決するため、我々は特定のランダム性を導入し、タイムアウトが同じではありませんすることができます。
第二は、ここで使用することがあるwhile(true)
ので、実際の開発ではプレーしてはなりません。このケースでは、再試行の最大数を設定する必要があります。
ナレーター:再試行非常に多くの時間が、成功していないが、ビジネスを成功させるのであれば。今成功していない、愚かなテストを持っていない、最初に置く、記録、〜聖歌を補償するために再試行します
次のようにライブロックコードを変更することができます。
public static final int MAX_TIME = 5;
public static void transfer(Account from, Account to, int amount) throws InterruptedException {
// 模拟正常的前置业务
TimeUnit.SECONDS.sleep(1);
// 保证转账一定成功
Random random = new Random();
int retryTimes = 0;
boolean flag=false;
while (retryTimes++ < MAX_TIME) {
// 等待时间随机
if (from.lock.tryLock(random.nextInt(1000), TimeUnit.MILLISECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock from account " + from.getCard());
if (to.lock.tryLock(random.nextInt(1000), TimeUnit.MILLISECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock to account " + to.getCard());
// 转出账号扣钱
from.addMoney(-amount);
// 转入账号加钱
to.addMoney(amount);
flag=true;
break;
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
}
if(flag){
System.out.println("transfer success");
}else {
System.out.println("transfer failed");
}
}
概要
デッドロックが発生した状況、日常の開発が容易である、我々は注意する必要があり、ロック解除のために注意を払います。ライブロックは、出会い異常な状況は、本質的に、我々は唯一の最大再試行回数を設定するには注意を払うに必要な、彼らは再試行されて永遠に落ちないことがあります。
参考リンク
http://c.biancheng.net/view/4786.html
https://www.javazhiyin.com/43117.html
インタプリタプログラム、および取得毎日ドライプッシュ:私は公共の数の関心を歓迎します。:あなたは私のトピックの内容に興味があるなら、あなたは私のブログに焦点を当てることができstudyidea.cn