ホームページ  >  記事  >  Java  >  Java同期の詳しい説明

Java同期の詳しい説明

高洛峰
高洛峰オリジナル
2016-12-13 10:55:141290ブラウズ

Java 言語キーワードをメソッドまたはコード ブロックの変更に使用すると、最大 1 つのスレッドが同時にコードを実行するようになります。

1. 2 つの同時スレッドが同じオブジェクト内の同期された (この) 同期コード ブロックにアクセスする場合、一度に 1 つのスレッドのみを実行できます。別のスレッドは、このコード ブロックを実行する前に、現在のスレッドがこのコード ブロックの実行を完了するまで待つ必要があります。

2. ただし、スレッドがオブジェクトの同期された (この) 同期コード ブロックにアクセスするとき、別のスレッドは引き続きオブジェクト内の非同期 (この) 同期コード ブロックにアクセスできます。

3. 特に重要なのは、スレッドがオブジェクトの同期された (この) 同期されたコード ブロックにアクセスすると、他のスレッドはオブジェクト内の他のすべての同期された (この) 同期されたコード ブロックにアクセスできなくなることです。

4. 3 番目の例は、他の同期コード ブロックにも適用できます。つまり、スレッドがオブジェクトの synchronized (this) 同期コード ブロックにアクセスすると、このオブジェクトのオブジェクト ロックを取得します。その結果、オブジェクト オブジェクトのすべての同期されたコード部分への他のスレッドのアクセスが一時的にブロックされます。

5. 上記のルールは他のオブジェクト ロックにも適用されます。

例:
1. 2 つの同時スレッドが同じオブジェクト オブジェクト内の同期された (この) 同期されたコード ブロックにアクセスする場合、一度に 1 つだけアクセスできます。スレッドが実行されます。別のスレッドは、このコード ブロックを実行する前に、現在のスレッドがこのコード ブロックの実行を完了するまで待つ必要があります。

パッケージ ths;

public class Thread1 は Runnable {
public void run() {
using using use using using using through out using out through ‐ ‐ ‐ ‐ ‐ , System.out.println(Thread .currentThread( ).getName() + " 同期ループ " + i);
スレッド ta = 新しいスレッド (t1, "A"); スレッド tb = 新しいスレッド (t1, "B");
A 同期ループ 1
A 同期ループ 2
A 同期ループ 3
A 同期ループ 4
B 同期ループ 0
B 同期ループ 1
B 同期ループ 2
B 同期ループ 3
B ronized ループ 4

2. ただし, スレッドがオブジェクトの synchronized (this) 同期コード ブロックにアクセスするとき、別のスレッドは引き続きオブジェクト内の非 synchronized (this) 同期コード ブロックにアクセスできます。

パッケージは

public class Thread2 {
public void m4t1() {
synchronized(this) {
int i = 5;  
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);  
{
Thread.sleep(500); を試してみましょう。  
} catch (InterruptedException ie) {
}
}
}
public void m4t2() {
int i = 5;  
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);  
{
Thread.sleep(500); を試してみましょう。  
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
最終 Thread2 myt2 = new Thread2();  
スレッド t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" );  
スレッド t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" );  
t1.start();  
t2.start();  
}
}

结結果:

t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0

第三に、他の重要な点は、あるオンライン プロセスがオブジェクトの 1 つの同期された (この) 同期コード ブロックを通過するとき、他のすべてのオブジェクト内の他のすべての同期された (この) 同期コード ブロックへのアクセスがブロックされることです。Re // Thread2.m4t2 () メソッドを変更します:

Public void M4T2 () {
Synchronized (this) {
int i = 5;
about (i- & gt; 0) {
System.out.println (Thread. currentThread().getName() + " : " + i); {
: 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3

t2:2

t2 : 1

t2 : 0

4. 3 番目の例は、他の同期コード ブロックにも適用できます。つまり、スレッドがオブジェクトの synchronized (this) 同期コード ブロックにアクセスすると、このオブジェクトのオブジェクト ロックを取得します。その結果、オブジェクト オブジェクトのすべての同期されたコード部分への他のスレッドのアクセスが一時的にブロックされます。


// Thread2.m4t2() メソッドを次のように変更します:

public synchronized void m4t2() .out.println(Thread.currentThread( ).getName() + " : " + i); }
}
}

結果:
t1 : 4
t1 : 3

t1 : 2

t1 : 1

t1 : 0

t2 : 4

t2 : 3

t2 : 2
t2 : t2:0

5.上記のルールは他のオブジェクト ロックにも適用されます:

package ths;

public class Thread3 {
class Inner {
private void m4t1() {
int i = 5; 
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); 
{
Thread.sleep(500); を試してみましょう。 
} catch(InterruptedException ie) {
}
}
}
private void m4t2() {
int i = 5; 
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); 
{
Thread.sleep(500); を試してみましょう。 
} catch(InterruptedException ie) {
}
}
}
}
プライベート void m4t1(inner inner) {
synchronized(inner) { //使用对象锁
inner.m4t1(); 
}
private void m4t2(インナー inner) {
inner.m4t2(); 
}
public static void main(String[] args) {
final Thread3 myt3 = new Thread3(); 
Final Inner inner = myt3.new Inner(); 
スレッド t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1"); 
スレッド t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2"); 
t1.start(); 
t2.start(); 
}
}

結果:

ライン プログラム t1 によって、Inner のオブジェクトが取得されましたが、ライン プログラム t2 は同じインナーの非同期部分であるため、2 つのライン プログラムは相互に干渉しません。 : インナー.m4t1()=4

t2 : Inner.m4t2()=4

t1 : Inner.m4t1()=3
t2 : Inner.m4t2()=3
t1 : Inner.m4t1()=2
t2 : インナー.m4t2()= 2
t1:inner.m4t1()= 1
t2:inner.m4t2()= 1 Inner.m4t2() 前面追加同期:

int i = 5; " + i);インナーで。

t1 : Inner.m4t1()=4
t1 : Inner.m4t1()=3
t1 : Inner.m4t1()=2
t1 : Inner.m4t1()=1
t1 : Inner.m4t 1() = 0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2

t2 : Inner.m4t2()=1

t2 : Inner.m4t2 () = 0

2 番目の記事:

synchronized キーワード。これには、synchronized メソッドと synchronized ブロックという 2 つの使用法が含まれます。
1. 同期メソッド: メソッド宣言に synchronized キーワードを追加して、同期メソッドを宣言します。例:
public synchronized void accessVal(int newVal);
同期メソッドはクラス メンバー変数へのアクセスを制御します。各クラス インスタンスはロックに対応し、各同期メソッドはメソッドを呼び出すクラス インスタンスのロックを取得する必要があります

実行しないと、そのメソッドが属するスレッドはブロックされます。メソッドが実行されると、ロックはメソッドから戻るまで解放されません。実行可能ファイル

状態に入ります。このメカニズムにより、各クラス インスタンスに対して、同期済みとして宣言されたすべてのメンバー関数のうち最大 1 つだけが実行可能状態になることが保証されます (クラス インスタンスに対応するロックを取得できるのは最大でも 1 つだけであるため)。これにより、Access が効果的に回避されます。クラス メンバー変数の違反 (クラス メンバー変数にアクセスできるすべてのメソッドが同期化されていると宣言されている限り)

Java では、クラス インスタンスだけでなく、各クラスもロックに対応します。このように、クラスの静的メンバー関数を同期済みとして宣言して、クラスの静的メンバー変数へのアクセスを制御することもできます。

synchronized メソッドの欠点: 大きなメソッドが synchronized として宣言されている場合、通常、スレッド クラスのメソッド run() が

synchronized として宣言されていると、ライフサイクル全体にわたって実行されているため、効率に大きな影響を与えます。スレッドが実行されるため、このクラスの同期メソッドへの呼び出しは失敗します。もちろん、クラス メンバー変数にアクセスするコードを特別なメソッドに組み込み、同期化を宣言し、メイン メソッドで呼び出すことでこの問題を解決できますが、Java はより良い解決策を提供します。解決策は同期化ブロックです。
2. 同期ブロック: synchronized キーワードを通じて同期ブロックを宣言します。構文は次のとおりです。
synchronized(syncObject) {
//アクセス制御を可能にするコード

}

synchronized ブロックは、コードがオブジェクト syncObject のロックを取得する必要があるコード ブロックです (前述したように、クラスインスタンスまたはクラス) が実行されるまでの具体的なメカニズムは上記と同じです。任意のコードブロックを対象とし、ロック対象を任意に指定できるため、柔軟性が高い。

synchronized(this) についての理解

1. 2 つの同時スレッドが同じオブジェクト内の synchronized(this) 同期コード ブロックにアクセスする場合、一度に 1 つのスレッドのみを実行できます。別のスレッドは、このコード ブロックを実行する前に、現在のスレッドがこのコード ブロックの実行を完了するまで待つ必要があります。

2. ただし、スレッドがオブジェクトの synchronized(this) 同期コード ブロックにアクセスするとき、別のスレッドは引き続きオブジェクト内の非同期

(this) 同期コード ブロックにアクセスできます。

3 番目に、特に重要なのは、スレッドがオブジェクトの synchronized(this) 同期コード ブロックにアクセスすると、他のスレッドはオブジェクト内の他のすべての synchronized(this) にアクセスすることです

同期されたコードブロックへのアクセスはブロックされます。
4. 3 番目の例は、他の同期されたコード ブロックにも適用できます。つまり、スレッドがオブジェクトの synchronized(this) 同期コード ブロックにアクセスすると、この

オブジェクトのオブジェクト ロックを取得します。その結果、オブジェクト オブジェクトのすべての同期されたコード部分への他のスレッドのアクセスが一時的にブロックされます。
5. 上記のルールは他のオブジェクトのロックにも適用されます

http://hi.baidu.com/sunshibing/blog/item/5235b9b731d48ff430add14a.html
Java での synchronized の使用法

例: オブジェクトは大きなオブジェクトのようなものです家、ドアはいつも開いています。家の中にはたくさんの部屋(つまりメソッド)があります。

これらの部屋はロックされているか (同期方式)、またはロックが解除されています (通常方式)。部屋のドアには鍵があり、この鍵ですべての施錠された部屋を開けることができます。

さらに、オブジェクトのメソッドを呼び出したいすべてのスレッドと、この家の特定の部屋に入ろうとしている人々を比較します。必要なのはこれだけです。これらがどのように連携するかを見てみましょう。

ここでまず前提条件を明確にします。オブジェクトには少なくとも 1 つの同期メソッドがあります。それ以外の場合、このキーは意味を持ちません。もちろん、私たちにはそのようなテーマはありません。

ある人が鍵のかかった部屋に入りたいと思って、家の玄関まで来て、そこに鍵があるのを見ました(まだ誰もその鍵のかかった部屋を使いたがっていないことを示しています)。そこで彼は歩いて行き、鍵

を手に入れ、計画通りに部屋を使いました。彼は施錠された部屋を使用するたびにすぐに鍵を返却することに注意してください。鍵のかかった部屋を2つ続けて使いたい場合でも、鍵を返却して真ん中に戻さなければなりません。

そのため、通常の鍵の使用原則は「使​​った分だけ借りて、使ったらすぐに返す」です

現時点では、鍵がかかっていない部屋は他の人も制限なく使用でき、一人でも使用できます。 1部屋、2名様までご利用可能で、制限はございません。しかし、鍵のかかった部屋に入りたい場合は、ドアまで走って確認する必要があります。鍵を持っていればもちろん持って帰ることができますが、鍵を持っていない場合は待つしかありません。

この鍵をたくさんの人が待っている場合、鍵が返されたときに誰が最初に鍵を受け取るでしょうか?保証されていません。先ほどの例で鍵のかかった部屋を 2 つ続けて使いたい男性のように、鍵を返却したときに他の人が鍵を待っている場合、その男性が再び鍵を入手できる保証はありません。 (JAVA 仕様では、

Thread.sleep() がブレーク後に実行に戻るまでの時間など、保証がないことが多くの場所で明確に述べられています。同じ優先度のどのスレッドが最初に実行されるか、いつロックされるかなど)アクセスすると、オブジェクトが解放されるか、待機プールに置かれるかなど、最終的な決定は JVM が行うことになると思います。判断条件が多すぎると、知的財産保護上の理由から、判断が行われる可能性もあります。無視されるのは当然です。しかし、コンピューターを研究したことがある人なら誰でも発見できるので、これらの不確実性は完全に不確かではないと思います。人が特定の方法で書いた乱数です。また、それを決定するのが面倒で意味がないからかもしれません。)

を見てみましょう。同期コードブロック。同期方法とは少し異なります。

1. サイズの点では、同期されたコード ブロックは同期されたメソッドよりも小さくなります。同期されたコード ブロックは、ロックされた画面で区切られたロックされていない部屋内のスペースと考えることができます。

2. 同期コード ブロックは、別のオブジェクトのキーを手動で指定することもできます。この画面の鍵を開けるのにどの鍵を使用できるかを指定するのと同じです。この家の鍵を使用して開くこともできます。この場合、

を使用して開くこともできます。別の家に行き、そこでその鍵を入手し、その家の鍵を使ってこの家のロックされた画面を開きます。手に入れた他の家の鍵は、家の鍵がかかっていない部屋に他の人が入ってくるのに影響を与えないことに注意してください。

同期されたコード ブロックを使用する理由は何ですか?私は次のようになるべきだと思います。まず、プログラムの同期部分は動作効率に影響します。メソッドは通常、最初にいくつかのローカル変数を作成し、次にこれらの変数に対して計算や表示などのいくつかの操作を実行します。 ; 同期の対象となるコードが増えるほど、効率への影響は大きくなります。そのため、私たちは通常、その影響範囲をできるだけ小さく抑えるよう努めます。

どうやってやるの?同期されたコードブロック。同期する必要があるメソッドの部分 (操作など) のみを同期します。

sych型コードブロックでキーを指定できるように、特定の期間内にオブジェクトのキーを占有できるという追加の利点があります。通常の状況でキーを使用する原則をまだ覚えていますか?これらは普通の状況ではありません。取得したキーは永久に返されるのではなく、同期されたコード ブロックを終了するときに返されます。连 2 つの更衣室を続けて使用したい男性を例に挙げます。 1つの部屋を使用した後、別の部屋を引き続き使用するにはどうすればよいですか?同期されたコード ブロックを使用します。まず別のスレッドを作成し、同期コード ブロックを作成し、そのコード ブロックのロックを家の鍵に向けます。次に、そのスレッドを開始します。そのコードブロックに入ったときにこの家の鍵

を手に入れることができる限り、そのコードブロックを出るまでそれを保持することができます。言い換えれば、この部屋のすべての鍵のかかった部屋を横断することも、睡眠することもできます (10*60*1000) が、部屋のドアで鍵を待っているスレッドがまだ

1000 個あります。とても楽しいです。 P Sleep() メソッドとキーの相関関係について話しましょう。スレッドがキーの取得後に強制的に sleep() され、同期コンテンツが完了していない場合、キーはそのまま残ります。キーは、再度実行されてすべての同期コンテンツが完了するまで返されません。覚えておいてください、その人は仕事で疲れて休憩しに行っただけで、やりたいことは終わっていませんでした。他人が部屋に入ってきて部屋が散らかることを防ぐため、寝るときも唯一の鍵を身に着けていました。最後に、なぜ鍵と 1 つのドアではなく鍵で開ける必要があるのか​​と疑問に思う人もいるかもしれません。これは純粋に複雑さの問題だと思います。 1つのドアに1つの鍵の方が確かに安全ですが、それには多くの問題が伴います。鍵の生成、保管、取得、返却など。同期方法の増加に伴い、その複雑さは幾何級数的に増加し、効率に重大な影響を与える可能性があります。これはトレードオフの問題とみなすことができます。少しでもセキュリティを高めるために効率を大幅に下げることは、なんと望ましくないことでしょう。

同期の簡単な例

public class TextThread {

public static void main(String[] args) {

TxtThread tt = new TxtThread();

new Thread(tt).start(); tt).start();

new Thread(tt).start();

}

クラス TxtThread は Runnable {

int num = 100; new String();

public void run() {

synchronized (str) {

while (num > 0) {

try {

Thread.sleep(1)

} catch (Exception e) {

e .getMessage();

}

System.out.println(Thread.currentThread().getName()

+ "this is " + num--); 上の例では、エラーの原因となる時間差を作るには、Thread.sleep(10) を使用します

Java のマルチスレッドサポートと同期メカニズムは、誰からも愛されており、synchronized キーワードを使用すると、マルチスレッドの共有を簡単に解決できるようです。データ同期の問題。それはどんな感じですか

? ——結論を出す前に、同期キーワードの役割を深く理解する必要があります。

一般に、synchronized キーワードは、関数の修飾子として、または関数内のステートメントとして使用できます。これは、通常、同期メソッドおよび同期ステートメント ブロックと呼ばれるものです。さらに分類すると、

synchronized はインスタンス変数、オブジェクト参照、静的関数、クラス リテラル (クラス名リテラル) に作用できます。


さらに詳しく説明する前に、いくつかの点を明確にする必要があります:

A. synchronized キーワードがメソッドに追加されるかオブジェクトに追加されるかに関係なく、コードの一部や関数をロックとして扱うのではなく、取得されるロックはオブジェクトです。また、synchronized メソッドは他のオブジェクトからアクセスされる可能性があります。スレッド。


B.各オブジェクトにはロックが 1 つだけ関連付けられています。

C.同期を実現するには多大なシステム オーバーヘッドが必要となり、デッドロックが発生する可能性もあるため、不必要な同期制御は避けるようにしてください。


次に、コード上のさまざまな場所での同期の影響について説明します。

P1 と P2 が同じクラスの異なるオブジェクトであると仮定します。このクラスは、P1 と P2 のすべての状況で同期ブロックまたは同期メソッドを定義します。呼ばれる。

1. 関数修飾子として synchronized を使用する場合のサンプルコードは次のとおりです。

これは同期方法ですが、この時点でどのオブジェクトが同期ロックされていますか?ロックするのは、この同期メソッドを呼び出すオブジェクトです。言い換えれば、オブジェクト P1 がこの同期メソッドを異なるスレッドで実行すると、それらは同期効果を達成するために相互排他を形成します。ただし、このオブジェクトが属するクラスによって生成された別のオブジェクトP2は、

synchronizedキーワードを追加してこのメ​​ソッドを任意に呼び出すことができます。

上記のコード例は、次のコードと同等です:

public void methodAAA()

{

synchronized (this) // (1)

{

//…..

}

}

(1)のこれは何を指しますか?これは、このメソッドを呼び出すオブジェクト (P1 など) を指します。同期メソッドの本質は、オブジェクト参照に synchronized を適用することであることがわかります。 ——P1 オブジェクト ロックを取得したスレッドのみが P1 の同期メソッドを呼び出すことができます。P2 に関しては、P1 ロックはそれとは関係がありません。また、プログラムは同期メカニズムの制御を取り除くこともできます。この状況ではデータの混乱を引き起こします: (

2. 同期ブロック、サンプルコードは次のとおりです:

public void method3(SomeObject so)

{

synchronized(so)

{

//…..

}

}

このとき、ロックはオブジェクトです。ロックを取得した人は、それによって制御されるコードを実行できます。ロックとして明確なオブジェクトがある場合は、次のようにプログラムを書くことができます。明確なオブジェクトはありません。
正確なオブジェクトがロックとして機能します。 コードの一部を同期したい場合は、ロックとして機能する特別なインスタンス変数 (オブジェクトである必要があります) を作成できます:

class Foo runnable {

プライベートバイト[] lock = new byte [0];バイトコード: 長さ 0 の byte[] オブジェクトの生成には 3 つのオペコードしか必要としませんが、Object lock

= new Object() には 7 行のオペレーション コードが必要です。 Foo

{

public synchronized static void methodAAA() // 同步的static関数数

{

//….

} V Public Void Methodbbb () {

Synchronized (foo.class) // クラスリテラル同期された静的関数としての効果。取得されるロックは非常に特殊です。このクラスによって生成された特定のオブジェクトではなく、現在このメソッドを呼び出しているオブジェクトが属するクラス (Class) です。 「効率的な Java」では、同期ロックに Foo.class と P1.getClass() を使用することは同じではありません。この Class


をロックする目的を達成するために P1.getClass() を使用することはできません。 P1 は、Foo クラスによって生成されたオブジェクトを参照します。

それは推測できます: 同期静的関数 A がクラスで定義され、同期インスタンス関数 B も定義されている場合、このクラスの同じオブジェクト Obj は複数のスレッドでそれぞれ A メソッドと B メソッドにアクセスします。ロックが異なるため、同期は構成されません。メソッド A のロックはオブジェクト Obj であり、メソッド B のロックは Obj が属するクラスです。

概要は次のとおりです:

どのオブジェクトが同期によってロックされているかを理解することは、より安全なマルチスレッド プログラムを設計するのに役立ちます。

共有リソースへの同期アクセスをより安全にするための技術もいくつかあります。

1. パブリック/保護されたインスタンス変数の代わりに、プライベート インスタンス変数とその get メソッドを定義します。変数が public として定義されている場合、同期メソッドの制御をバイパスして、オブジェクトを外部から直接取得して変更できます。これも JavaBean の標準的な実装方法の 1 つです。

2. インスタンス変数が配列や ArrayList などのオブジェクトである場合、上記のメソッドは依然として安全ではありません。外部オブジェクトが get メソッドを通じてインスタンス オブジェクトの参照を取得し、それが別のオブジェクトを指す場合、このプライベート変数も変更されており、非常に危険です。 現時点では、get メソッドに同期を追加し、このプライベート オブジェクトの clone() のみを返す必要があります。このようにして、呼び出し元が取得するのはオブジェクト copy への参照です

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