(三)synchronizedを用いてスレッド間の通信を実現する
9231 ワード
スレッドとスレッドの間で実行されるタスクは異なりますが、スレッドとスレッドの間で動作するデータは同じです.
1、構築例
マルチスレッドが学生の情報を同時に読み取り、出力することを実現する1.学生を格納するためのクラスを作成する.2.入力タスクを保存するクラスを作成します.3.出力タスクを保存するクラスを作成します.4.パラメータ伝達方式を用いて、2つのスレッドに同じパラメータを使用させ、スレッド間の通信を実現する.
結果は次のとおりです.
「トム」、「男」、「Monica」、「女」の情報が入っていたはずなのに、エラーが発生しました.このようなエラーの原因は、入力スレッドが「Tom」、「男」を記憶した後、下方向に動作し、iの値を0に変更してループを継続し、i=0のときに「Monica」を記憶した直後にCPUが出力スレッドに奪われたが、入力スレッドが「女」を記憶していないため、出力スレッドが「Monica」、「男」を出力したためである.
2、共有データのセキュリティ問題
学生の情報はこの例のコードの中で共有データに属して、入力スレッドと出力スレッドが共同で操作して、どのようにデータの安全を実現して、前の誤りを避けて、synchronizedコードブロックを使ってタスクコードを囲んで、以下のように修正することができます:
結果は次のとおりです.
synchronizedを使用して同期ロックを実現する2つの条件:1つ目は2つ以上のスレッドが存在し、2つ目は複数のスレッドが同じロックを使用することです.ここではまず入力と出力の2つのスレッドがあり,次に,スレッドが使用するロックはいずれもstuオブジェクトであり,ロックの統一を実現し,効果を保証した.
3、スレッド待ちとスレッド起動の設定
修正の結果、エラーは解消されたものの、同じ学生情報を長時間出力し続けることになります.この場合、出力スレッドがCPUを占有して出力されるため、パフォーマンスの無駄になります.これを避けるために、1つのデータを格納したときに、もう1つのデータを出力することを実現します.待機起動メカニズムを使用することができます.コードは以下の通りです.
結果は次のとおりです.
ウェイクアップ待ちメカニズムを使用すると、1つのデータを格納してから1つのデータを出力する効果を実現し、ウェイクアップ待ちメカニズムを簡単にまとめます.
wait()メソッド、notify()メソッド、notifyAll()メソッドは同期コードに使用する必要があります.同期コードにのみロックがありますが、この3つのメソッドはロックを使用して呼び出す必要があります.どのスレッドがロックを持っているか、どのスレッドが待機または起動状態になります.**wait()メソッド:**スレッドを待機状態にし、実際にスレッドをスレッドプールに入れます. notify()メソッド:スレッドプール内の任意のスレッドを起動し、特定のロックを持つスレッドであるが、複数のスレッドが同じロックを持つことができるため、実際には任意の特定のロックを持つスレッドを起動する. notifyAll()メソッド:すべてのスレッドを起動します.
この3つのメソッドをObjectクラスに定義するのは、いずれのオブジェクトもロックとして使用できるため、いずれのオブジェクトもこの3つのメソッドを呼び出すことができるため、すべてのクラスの親、すなわちObjectクラスでのみ定義できます.
4、最適化完備サンプルコード
注意:JKDバージョンの更新に伴い、1.5バージョン以降synchronizedよりも強力な同期ロックを実現する方法が現れ、詳細はロックインタフェースとConditionインタフェースを使用して生産者と消費者を実現することを参照してください.
1、構築例
マルチスレッドが学生の情報を同時に読み取り、出力することを実現する1.学生を格納するためのクラスを作成する.2.入力タスクを保存するクラスを作成します.3.出力タスクを保存するクラスを作成します.4.パラメータ伝達方式を用いて、2つのスレッドに同じパラメータを使用させ、スレッド間の通信を実現する.
// ,
class Student {
String name;
String sex;
}
//
class Input implements Runnable {
/*
*
* 2
*
*/
private Student stu;
public Input(Student stu) {
this.stu = stu;
}
public void run() {
// , if
int i = 1;
// , while
while (true) {
if (i == 1) {
stu.name = "Tom";
stu.sex = " ";
} else {
stu.name = "Monica";
stu.sex = " ";
}
// i , i 1 0
i = (i + 1) % 2;
}
}
}
//
class Output implements Runnable {
private Student stu;
public Output(Student stu) {
this.stu = stu;
}
public void run() {
// while
while (true) {
System.out.println(stu.sex + " :" + stu.name);
}
}
}
public class Demo1 {
public static void main(String[] args) {
//
Student stu = new Student();
// , stu
Input in = new Input(stu);
// , stu
Output out = new Output(stu);
//
Thread t1 = new Thread(in);
//
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
結果は次のとおりです.
「トム」、「男」、「Monica」、「女」の情報が入っていたはずなのに、エラーが発生しました.このようなエラーの原因は、入力スレッドが「Tom」、「男」を記憶した後、下方向に動作し、iの値を0に変更してループを継続し、i=0のときに「Monica」を記憶した直後にCPUが出力スレッドに奪われたが、入力スレッドが「女」を記憶していないため、出力スレッドが「Monica」、「男」を出力したためである.
2、共有データのセキュリティ問題
学生の情報はこの例のコードの中で共有データに属して、入力スレッドと出力スレッドが共同で操作して、どのようにデータの安全を実現して、前の誤りを避けて、synchronizedコードブロックを使ってタスクコードを囲んで、以下のように修正することができます:
//
class Input implements Runnable {
private Student stu;
public Input(Student stu) {
this.stu = stu;
}
public void run() {
int i = 1;
while (true) {
// synchronized
synchronized (stu) {
if (i == 1) {
stu.name = "Tom";
stu.sex = " ";
} else {
stu.name = "Monica";
stu.sex = " ";
}
}
i = (i + 1) % 2;
}
}
}
//
class Output implements Runnable {
private Student stu;
public Output(Student stu) {
this.stu = stu;
}
public void run() {
while (true) {
// synchronized
synchronized (stu) {
System.out.println(stu.sex + " :" + stu.name);
}
}
}
}
結果は次のとおりです.
synchronizedを使用して同期ロックを実現する2つの条件:1つ目は2つ以上のスレッドが存在し、2つ目は複数のスレッドが同じロックを使用することです.ここではまず入力と出力の2つのスレッドがあり,次に,スレッドが使用するロックはいずれもstuオブジェクトであり,ロックの統一を実現し,効果を保証した.
3、スレッド待ちとスレッド起動の設定
修正の結果、エラーは解消されたものの、同じ学生情報を長時間出力し続けることになります.この場合、出力スレッドがCPUを占有して出力されるため、パフォーマンスの無駄になります.これを避けるために、1つのデータを格納したときに、もう1つのデータを出力することを実現します.待機起動メカニズムを使用することができます.コードは以下の通りです.
// ,
class Student {
String name;
String sex;
/*
*
* flag
* false
*/
boolean flag;
}
//
class Input implements Runnable {
private Student stu;
public Input(Student stu) {
this.stu = stu;
}
public void run() {
int i = 1;
while (true) {
synchronized (stu) {
/*
*
* flag true
*
*
*
* flag false
* wait()
*
* else
*/
if(stu.flag){
try {
/*
* wait()
* run()
* try-catch
*
* wait()
* ,
* stu
*
*/
stu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i == 1) {
stu.name = "Tom";
stu.sex = " ";
} else {
stu.name = "Monica";
stu.sex = " ";
}
// flag
stu.flag = true;
/*
*
*
* wait()
* notify()
*
* stu
*/
stu.notify();
}
i = (i + 1) % 2;
}
}
}
//
class Output implements Runnable {
private Student stu;
public Output(Student stu) {
this.stu = stu;
}
public void run() {
while (true) {
synchronized (stu) {
/*
*
* flag true
* false
*
* ture
*
*/
if(!stu.flag){
try {
stu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(stu.sex + " :" + stu.name);
// flag
stu.flag = false;
// ,
stu.notify();
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
//
Student stu = new Student();
//
Input in = new Input(stu);
//
Output out = new Output(stu);
//
Thread t1 = new Thread(in);
//
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
結果は次のとおりです.
ウェイクアップ待ちメカニズムを使用すると、1つのデータを格納してから1つのデータを出力する効果を実現し、ウェイクアップ待ちメカニズムを簡単にまとめます.
wait()メソッド、notify()メソッド、notifyAll()メソッドは同期コードに使用する必要があります.同期コードにのみロックがありますが、この3つのメソッドはロックを使用して呼び出す必要があります.どのスレッドがロックを持っているか、どのスレッドが待機または起動状態になります.
この3つのメソッドをObjectクラスに定義するのは、いずれのオブジェクトもロックとして使用できるため、いずれのオブジェクトもこの3つのメソッドを呼び出すことができるため、すべてのクラスの親、すなわちObjectクラスでのみ定義できます.
4、最適化完備サンプルコード
class Student {
private String name;
private String sex;
private boolean flag;
//
public synchronized void set(String name, String sex) {
if (flag) {
try {
wait();// this,
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
//
public synchronized void out() {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(sex + " :" + name);
flag = false;
notify();
}
}
//
class Input implements Runnable {
private Student stu;
public Input(Student stu) {
this.stu = stu;
}
public void run() {
int i = 1;
while (true) {
if (i == 1) {
stu.set("Tom", " ");
} else {
stu.set("Monica", " ");
}
i = (i + 1) % 2;
}
}
}
//
class Output implements Runnable {
private Student stu;
public Output(Student stu) {
this.stu = stu;
}
public void run() {
while (true) {
stu.out();
}
}
}
public class Demo1 {
public static void main(String[] args) {
Student stu = new Student();
Input in = new Input(stu);
Output out = new Output(stu);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
注意:JKDバージョンの更新に伴い、1.5バージョン以降synchronizedよりも強力な同期ロックを実現する方法が現れ、詳細はロックインタフェースとConditionインタフェースを使用して生産者と消費者を実現することを参照してください.
: , , , ! ↓↓↓