1. 程式人生 > >Java線程與並發編程實踐----等待通知(生產者消費者問題)線程

Java線程與並發編程實踐----等待通知(生產者消費者問題)線程

static string @override 現在 循環 urn sum inter 一個

Java提供了一套API來支持線程之間的交互。在Object類中提供了一套等待通知的API

wait()

notify()

notifyAll()

此處要註意的是,絕不要在循環外面調用wait()方法。(單獨開一片文章來討論)


下面使用消費者與生產者問題來展示以上API的使用:

package xiancheng;

public class PC {

	public static void main(String[] args) {
		Shared s = new Shared();
		Thread t1 = new Thread(new Product(s));
		Thread t2 = new Thread(new Consumer(s));
		t1.start();
		t2.start();
	}
}
class Shared {
	
	private char c;
	private volatile boolean writeable = true;
	
	public synchronized void setChar(char ch) {
		while(!writeable) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.c = ch;
		writeable = false;
		notify();
	}
	
	public synchronized char getChar() {
		while(writeable) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		writeable = true;
		notify();
		return c;
	}
}

class Product implements Runnable{
	
	private Shared s;
	public Product(Shared s) {
		this.s = s;
	}

	@Override
	public void run() {
		for (char i = 'A'; i < 'Z'; i++) {
			s.setChar(i);
			System.out.println("生產者生產了一個" + i);
		}
	}
	
}

class Consumer implements Runnable{
	
	private Shared s;
	public Consumer(Shared s) {
		this.s = s;
	}

	@Override
	public void run() {
		char ch;
		do {
			ch = s.getChar();
			System.out.println("消費者消費了一個" + ch);
		} while (ch != 'Z');
	}
}

打印結果:

消費者消費了一個A
生產者生產了一個A
生產者生產了一個B
消費者消費了一個B
生產者生產了一個C
消費者消費了一個C
生產者生產了一個D
消費者消費了一個D
生產者生產了一個E
消費者消費了一個E
生產者生產了一個F
消費者消費了一個F
生產者生產了一個G
消費者消費了一個G
生產者生產了一個H
消費者消費了一個H
生產者生產了一個I
消費者消費了一個I
生產者生產了一個J
消費者消費了一個J
生產者生產了一個K
消費者消費了一個K
生產者生產了一個L
生產者生產了一個M
消費者消費了一個L
消費者消費了一個M
生產者生產了一個N
消費者消費了一個N
生產者生產了一個O
消費者消費了一個O
生產者生產了一個P
消費者消費了一個P
生產者生產了一個Q
消費者消費了一個Q
生產者生產了一個R
消費者消費了一個R
生產者生產了一個S
消費者消費了一個S
生產者生產了一個T
消費者消費了一個T
生產者生產了一個U
消費者消費了一個U
生產者生產了一個V
消費者消費了一個V
生產者生產了一個W
消費者消費了一個W
生產者生產了一個X
消費者消費了一個X
生產者生產了一個Y
消費者消費了一個Y

很明顯第一二行就出現了問題,消費出現在了生產之前,查看代碼就知道,生產與消費的順序並沒有錯亂,只是打印順序不對而已,因為喚醒動作是在打印之前,

改進代碼如下:

package xiancheng;

public class PC {

	public static void main(String[] args) {
		Shared s = new Shared();
		Thread t1 = new Thread(new Product(s));
		Thread t2 = new Thread(new Consumer(s));
		t1.start();
		t2.start();
	}
}
class Shared {
	
	private char c;
	private volatile boolean writeable = true;
	
	public synchronized void setChar(char ch) {
		while(!writeable) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.c = ch;
		writeable = false;
		notify();
	}
	
	public synchronized char getChar() {
		while(writeable) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		writeable = true;
		notify();
		return c;
	}
}

class Product implements Runnable{
	
	private final Shared s;
	public Product(Shared s) {
		this.s = s;
	}

	@Override
	public void run() {
		for (char i = 'A'; i < 'Z'; i++) {
			synchronized(s) {
				s.setChar(i);
				System.out.println("生產者生產了一個" + i);
			}
		}
	}
	
}

class Consumer implements Runnable{
	
	private final Shared s;
	public Consumer(Shared s) {
		this.s = s;
	}

	@Override
	public void run() {
		char ch;
		do {
			synchronized(s) {
				ch = s.getChar();
				System.out.println("消費者消費了一個" + ch);
			}
		} while (ch != 'Z');
	}
}

運行結果:

生產者生產了一個A
消費者消費了一個A
生產者生產了一個B
消費者消費了一個B
生產者生產了一個C
消費者消費了一個C
生產者生產了一個D
消費者消費了一個D
生產者生產了一個E
消費者消費了一個E
生產者生產了一個F
消費者消費了一個F
生產者生產了一個G
消費者消費了一個G
生產者生產了一個H
消費者消費了一個H
生產者生產了一個I
消費者消費了一個I
生產者生產了一個J
消費者消費了一個J
生產者生產了一個K
消費者消費了一個K
生產者生產了一個L
消費者消費了一個L
生產者生產了一個M
消費者消費了一個M
生產者生產了一個N
消費者消費了一個N
生產者生產了一個O
消費者消費了一個O
生產者生產了一個P
消費者消費了一個P
生產者生產了一個Q
消費者消費了一個Q
生產者生產了一個R
消費者消費了一個R
生產者生產了一個S
消費者消費了一個S
生產者生產了一個T
消費者消費了一個T
生產者生產了一個U
消費者消費了一個U
生產者生產了一個V
消費者消費了一個V
生產者生產了一個W
消費者消費了一個W
生產者生產了一個X
消費者消費了一個X
生產者生產了一個Y
消費者消費了一個Y


Java線程與並發編程實踐----等待通知(生產者消費者問題)線程