JAVASE小白學記 多執行緒間通訊經典練習題
阿新 • • 發佈:2021-02-19
多執行緒間通訊經典練習題
上節我們大家一起探索了多執行緒通訊問題,瞭解了執行緒之間的等待喚醒機制。下面我們也可以通過一些練習題更加深入的去理解多執行緒之間的通訊問題。
1.兩個執行緒間的通訊問題
寫兩個執行緒,一個執行緒列印 1—52
一個執行緒列印 A—Z
列印結果是: 12A34B56C78D710E------5152Z
分析:
- 方式一:利用繼承Thread類來實現兩個執行緒間的通訊問題
public class MyObject {
//定義一個標記
public boolean flag=false;
}
//列印字母的執行緒
public class PrintCharacterThread extends Thread{
private MyObject myObject;
public PrintCharacterThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for(int i='a';i<='z';i++){
synchronized (myObject){
if(myObject.flag!=true){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println((char) i);
//修改標記
myObject.flag=false;
//喚醒列印數字執行緒
myObject.notify();
}
}
}
}
//列印數字的執行緒
public class PrintDitigalThread extends Thread {
private MyObject myObject;
public PrintDitigalThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for(int i=1;i<=52;i++){
synchronized (myObject){
if(myObject.flag!=false){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i);//1 2
if(i%2==0){
//修改標記
myObject.flag=true;
//喚醒列印字母的執行緒
myObject.notify();
}
}
}
}
}
//測試類
public class MyTest {
public static void main(String[] args) {
//建立一個類的物件來充當鎖物件
MyObject myObject = new MyObject();
PrintDitigalThread th1 = new PrintDitigalThread(myObject);
PrintCharacterThread th2 = new PrintCharacterThread(myObject);
th1.start();
th2.start();
}
}
- 方式2:利用實現Runnable介面方式實現兩個執行緒之間的通訊問題
//列印數字的執行緒
public class PrintDitigalRunnable implements Runnable {
private MyObject myObject;
public PrintDitigalRunnable(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 1; i <=52; i++) {
synchronized (myObject){
if(myObject.flag!=false){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i); //1 2
if(i%2==0){
//修改標記
myObject.flag=true;
//喚醒列印字母的執行緒
myObject.notify();
}
}
}
}
}
//列印字母的執行緒
public class PrintCharcterRunnable implements Runnable {
private MyObject myObject;
public PrintCharcterRunnable(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for(int i='a';i<='z';i++){
synchronized (myObject){
if(myObject.flag!=true){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print((char)i);
//修改標記
myObject.flag=false;
//喚醒列印數字的執行緒
myObject.notify();
}
}
}
}
public class MyObject {
//定義一個標記
public boolean flag=false;
}
public class MyTest {
public static void main(String[] args) {
MyObject myObject = new MyObject();
PrintCharcterRunnable printCharcterRunnable = new PrintCharcterRunnable(myObject);
Thread th1 = new Thread(printCharcterRunnable);
PrintDitigalRunnable printDitigalRunnable = new PrintDitigalRunnable(myObject);
Thread th2 = new Thread(printDitigalRunnable);
th1.start();
th2.start();
}
}
執行後的結果為:
2.三個執行緒之間的通訊問題
建立三個執行緒:一個執行緒列印 100個A,一個執行緒列印 100 個 B ,一個執行緒列印 100個C
輸出效果:ABC ABC ABC…交替列印
public class MyObject {
//定義一個標記
public int flag=1;
}
//列印字母A的執行緒
public class PrintAThread extends Thread {
//將鎖物件設定為成員屬性
private MyObject myObject;
//有參構造方法
public PrintAThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (myObject) {
//當標記對3取餘不等於1時,執行緒處於阻塞的狀態
//這裡的判斷語句用while而不用if,否則容易導致虛假喚醒的現象發生
while (myObject.flag % 3 != 1) {
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("A");
//修改標記
myObject.flag++;
//喚醒其他執行緒
//因為這裡的執行緒超過2個,所以喚醒語句需要用notifyAll()方法進行喚醒
myObject.notifyAll();
}
}
}
}
//列印字母B的執行緒
public class PrintBThread extends Thread {
//將鎖物件設定為成員屬性
private MyObject myObject;
//有參構造方法
public PrintBThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (myObject){
//當標記對3取餘不等於2時,當前執行緒處於阻塞的狀態
while (myObject.flag%3!=2){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("B");
//修改標記
myObject.flag++;
//喚醒其他執行緒
//因為這裡的執行緒超過2個,所以喚醒語句需要用notifyAll()方法進行喚醒
myObject.notifyAll();
}
}
}
}
//列印字母C的執行緒
public class PrintCThread extends Thread {
//將鎖物件設定為成員屬性
private MyObject myObject;
//有參構造方法
public PrintCThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (myObject){
//當標記對3取餘不等於2時,當前執行緒處於阻塞的狀態
while (myObject.flag%3!=0){
try {
myObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("C"+" ");
//修改標記
myObject.flag++;
//喚醒其他執行緒
//因為這裡的執行緒超過2個,所以喚醒語句需要用notifyAll()方法進行喚醒
myObject.notifyAll();
}
}
}
}
public class MyTest {
public static void main(String[] args) {
MyObject myObject = new MyObject();
PrintAThread printAThread = new PrintAThread(myObject);
PrintBThread printBThread = new PrintBThread(myObject);
PrintCThread printCThread = new PrintCThread(myObject);
printAThread.start();
printBThread.start();
printCThread.start();
}
}
輸出的結果為:
總結
以上就是多執行緒通訊方面的兩個經典的例題,利用的還是執行緒之間的等待喚醒機制,需要注意的是當超過兩個執行緒進行通訊時,使用的喚醒語句為notifyAll()方法而不是notify()方法。