一般的な概念をロック
- 相互に排他的:その実行の唯一のスレッド
- クリティカルセクション:実行する必要のあるコードのミューテックスセクション
- ファイングレインロック:別のロックと保護されたリソース細心の管理。ファイングレインロックは、並列度を高めることができ、パフォーマンスの最適化が重要な手段であります
- デッドロック:スレッドのグループは、「恒久的な」ブロッキング現象につながる、お互いのために待機している各他とのリソースの競合します。
ロックとベストプラクティス
- ロックが常にあるときのみ、オブジェクトのメンバ変数を更新します。
- 変数のメンバ変数にアクセスするときは、必ずのみロックします。
- ロックされたときに別のオブジェクトのメソッドを呼び出すことはありません。
- ロックの粒度を減らす、減少ホールド時間を結果。
同期および非同期
- あなたは結果を得るための方法の待機を呼び出す必要がある場合は同期され、あなたは結果を待たない場合は非同期です。
- Javaコードの同期は、デフォルトのアプローチです。
非同期をサポートするプログラムを実装する方法:
- 非同期呼び出し:子スレッドを作成するために、発信者は、子スレッドの実行メソッド呼び出し。
- 非同期メソッド:呼び出し先;メソッドを実装したときに、メインロジック、メインスレッドダイレクトリターンを実行するためにスレッドを作成するために表示されます。
同期
class X{
//修饰非静态方法
synchronized void foo(){
//临界区
}
//修饰静态方法
synchronized static void bar(){
//临界区
}
//修饰代码块
Object obj = new Object();
void baz(){
synchronized(obj){
//临界区
}
}
}
Javaコンパイラは、自動的に(ロックを施錠追加)と(ロック解除ロックを解除)コード変更の同期方法やブロックの前と後に、そうすることの利点は、(ロックをロックしている)と(ロック解除ロックを解除)した後、すべてのペアで発生する必要がありますロック解除()が、(唯一の死者を待つことができる他のスレッドを意味する)致命的なバグのロックを解除することを忘れないでください。
変更された静的メソッド:
//修饰静态方法是用当前类的字节码文件作为锁
class X{
//修饰静态方法
synchronized(X.class) static void bar(){
//临界区
}
}
非静的メソッドは、更新日:
//修饰非静态方法是用当前对象作为锁
class X{
//修饰非静态方法
synchronized(this) static void bar(){
//临界区
}
}
ロックは、複数のリソースを保護する方法
資源の保護とロックの間に合理的な関係の対象は、Nでなければなりません:あなたはより多くのリソースを確保するためにロックを使用できることを意味しますが、リソースを保護するためにロックを超えて使用することはできません1の関係、
正しい姿勢ロックを使用します
転送事業に従い、例として、
例1:
public class Account {
/**
*锁:保护账⼾余额
*/
private final Object balLock = new Object();
/**
* 账⼾余额
*/
private Integer balance;
/**
* 错误的做法
* 非静态方法的锁是this,
* this这把锁可以保护自己的余额this.balance,保护不了别人的余额 target.balance
*
*/
synchronized void transfer(Account target,int amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;//这段代码会出现线程安全,要保证线程安全的话要使用同一个锁
}
}
}
例2:
public class Account {
/**
*锁:保护账⼾余额
*/
private final Object balLock = new Object();
/**
* 账⼾余额
*/
private Integer balance;
/**
* 正确的做法,但是会导致整个转账系统的串行
*
* Account.class是所有Account对象共享的,
* 而且这个对象是Java虚拟机在加载Account类的时候创建的,
* 所以我们不用担心它的唯一性
*
* 这样还有个弊端:所有的转账都是串行了
*/
void transfer2(Account target,int amt){
synchronized(Account.class){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
転送操作がシリアルになってきている。この場合には、通常のロジックは、アカウントにロックすべきであると口座に移し、他の動作に影響を与えることなく転送します。マイナーリフォーム:
例3:
public class Account {
/**
*锁:保护账⼾余额
*/
private final Object lock;
/**
* 账⼾余额
*/
private Integer balance;
//私有化无参构造
private Account(){}
//设置一个传递lock的有参构造
private Account(Object lock){
this.lock = lock;
}
/**
* 转账
*/
void transfer(Account target,int amt){
//此处检查所有对象共享锁
synchronized(lock){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
この問題を解決することができますが、それは、このメソッドは、同じオブジェクトを渡す必要がある場合にAccountオブジェクトを作成する必要があり、
あまりにも面倒実現可能性の退屈な欠如を書いて、オブジェクトを渡すことがあります。
例4:
public class Account {
/**
* 账⼾余额
*/
private Integer balance;
/**
* 转账
*/
void transfer(Account target,int amt){
//此处检查所有对象共享锁
synchronized(Account.class){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
共有ロックとしてAccount.classでは、ロック範囲が大きすぎます。Account.classは、すべてのAccountオブジェクトによって共有され、そしてこのオブジェクトは、Accountクラスをロードするときに作成したJava仮想マシンですので、我々はその独自性を心配しないでください。共有ロックとして使用Account.class、我々はあなたがオブジェクトを作成するときにアカウントを渡す必要はありません。
だから、新しい問題が並行プログラムは問題ではありませんが、銀行内部の事業譲渡の問題を解決するためにAccount.classのミューテックスを持つかのように発生するが、すべてのアカウントの転送動作は、口座振替Aとして、シリアルであることをアカウントB、C口座振替口座振替操作Dの両方の現実の世界では、平行であるが、彼が連載されたこのプログラムでは、このような場合、パフォーマンスの低下です。この方法は、対価の額によって複雑になるのであればOKではありません
正しい表現は、(きめ細かいロッキングを使用する)ことです。
例5:
public class Account {
/**
* 账⼾余额
*/
private Integer balance;
/**
* 转账
*/
void transfer(Account target,int amt){
//锁定转出账户
synchronized(this){
//锁住转入账户
synchronized(target){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
}
私たちが実際にそこに会計帳簿の形であり、各アカウントの本を持って、これらの本は、統一されたファイルラックに格納されていることを情報がない、古代に考えてみましょう。銀行出納係は、私たちに与えたファイル棚や書籍を消しに行く転送が手に本を取得したとき、その後、転送を行います。出納係は彼女が本を取った次の3つの場合に発生することがあります。
- ファイル棚や書籍アウトはまさに、その後離れている間、ブックに変身します。
- ファイルラックは書籍や本の一つに変身し、出納係は、他の本に送り返され、他の出納係を待っている間にいくつかのファイルの棚の本は、上で私たちの手を得る置くことにした場合。
- ていない図書のうち、書籍の中には、二冊の本を待って、この出納係が戻って送信されます。
ファイングレインロックがデッドロックする可能性
- デッドロック:スレッドのグループは、「恒久的な」ブロッキング現象につながる、お互いのために待機している各他とのリソースの競合します。
- お互いのリソースを保持している2つのスレッドが、デッドロックにつながるお互いから解放されていません
- デッドロックにつながることができ、きめ細かいロックを使用します
顧客を着座転送することがある場合は窓口サービスを見つける:他の顧客がジョン・ドウはまた、出納転送サービスかもしれ見つけたとき、転送アカウントがBに100元を、アカウント:振替口座の口座Bを100元、これと同時に張とLiファイル棚の本を取りに行く、誤っジョー・スミスがそこに着いた可能性があり、この時の本、ジョン・ドウは、ブックBを得ました ジョー・スミスがジョン・ドウは、図書B A(ジョー・スミスが奪われてい冊)後に本を取得するために待っている間、彼らは待たなければならない、(Bの本ジョン・ドウが奪われました)図書の後にBをより多くの本を得ましたどのくらい?ジョー・スミスは図書A、Bジョン・ドウを返送しないでもバック本をお送りしますので、彼らは...永遠に待っています。私たちは、暫定的に死んで待機と呼ばれます。
デッドロックを回避する方法
- 相互に排他的、共有リソースのXとYは、唯一のスレッドによって占有することができます。
- ;スレッドT1は、共有リソースを待っている間に、YをリソースXを共有するためになされたものであり、共有リソースxを放出せず、所持して待ちます
- つかむことができない、他のスレッドは、リソーススレッドT1の所持をつかむことを強制することはできません。
- 円形待ち、リソーススレッドスレッドT2のT1の所持を待って、リソースのスレッド1つのスレッドT2の所持を待って、循環が待っています。
限り1は、被害のデッドロックを回避することができますよう
待って - 通知メカニズムを
通知メカニズム - 同期で待機を実装
- 待機と同期()、NOTIF()、のnotifyAll()これらの三つの方法を容易に実現することができます。
- 待って():現在のスレッドのロックを解除、ブロックされた状態に入ります
- )(NOTIF、notifAll():スレッドがブロックされたという通知は、実行可能状態に、スレッドを実行し続けることができます
- (NOTIF)ランダムに悪いチームのために待機しているスレッドを通知します
- notifyAll()キュー内で待機しているすべてのスレッドに通知し、)(notifAllを使用することをお勧めします
待つとの違いを眠ります:
睡眠はオブジェクトのメソッドで、スレッドの方法を待ちます
ロックを解除しません睡眠、ロックを解除します待ちます
覚醒、睡眠時間の設定、目を覚ますために時間NOTIF必要待ちます
例外をキャッチするために待つ必要なく、睡眠の必要なし
)(待つ:現在のスレッドをブロックに
****コードワードは、あなたが私に懸念を与えるのを助けるために持っていることは容易ではない場合は****
****愛の生活愛の技術QQグループ:894 109 590 ****