Java—線程的通信
概念
線程通信概念:線程是操作系統中獨立的個體,但這些個體如果不經過特殊處理就不能成爲一個整體,線程間的通信就成爲整體的必用方式之一。當線程存在通信指揮,系統間的交互性會更強大,在提高CPU利用率的同時還會使開發人員對線程任務在處理過程中進行有效的把控與監督。
引入
問題引入:使用兩個線程打印1-100。線程1,線程2交替打印。
代碼:
package com.CharlesLC_Test;
public class Thread_Test2 {
public static void main(String[] args) {
Count demo = new Count();
Thread count_num1 = new Thread(demo);
Thread count_num2 = new Thread(demo);
count_num1.setName("線程一");
count_num2.setName("線程二");
count_num1.start();
count_num2.start();
}
}
//定義子類,實現Runnable接口
class Count implements Runnable{
private int num;
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
if (num <= 100)
System.out.println(Thread.currentThread().getName() + ": " + num++);
else
break;
}
}
}
}
對於不加任何措施的話,輸出結果並沒有依次輸出,這是因爲這兩個線程都在奪取cpu的執行權,這是一個概率性的:
wait()與notify()和notifyAll()
- wait():令當前線程掛起並放棄CPU、同步資源並等待,使別的線程可訪問並修改共享資源,而當前線程排隊等候其他線程調用notify()或notifyAll()方法喚醒,喚醒後等待重新獲得對監視器的所有權後才能繼續執行。
- notify():喚醒正在排隊等待同步資源的線程中優先級最高者結束等待
- notifyAll():喚醒正在排隊等待資源的所有線程結束等待.
注意:這三個方法只有在synchronized方法或synchronized代碼塊中才能使用,否則會報java.lang.llegalMonitorStateException異常。
因爲這三個方法必須有鎖對象調用,而任意對象都可以作爲synchronized的同步鎖,因此這三個方法只能在Object類中聲明。
利用wait()和notify()解決
修改Count類,將wait()和notify()加入其中
class Count implements Runnable{
private int num;
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
notify();
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ": " + num++);
}
else{break;}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
結果如下(後面就交替輸出了):
解釋:這裏結果顯示線程二先進入循環中,他先遇到synchronized,它拿到了對象鎖,那麼線程1就進不來,當線程二遇到notify()的時候,因爲沒有正在被阻塞的線程,所以無需做什麼。當線程二輸出0的時候,它遇到wait()線程二被阻塞,並且釋放對象鎖,線程一就可以進來,然後線程一遇到notify(),幫線程二結束等待,然後線程一遇到wait(),線程一被阻塞 ,等待線程二遇到notify()來結束線程一的等待…(之後一直這樣進行)
生產者和消費者模式
問題:生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品,店員一次只能持有固定數量的產品(比如:20),如果生產者試圖生產更多的產品,店員會叫生產者停一下,如果店中有空位放產品了再通知生產者繼續生產;如果店中沒有產品了,店員會告訴消費者等一下,如果店中有產品了再通知消費者來取走產品。
- 這裏可能出現兩個問題:
生產者比消費者快時,消費者會漏掉一些數據沒有取到。
消費者比生產者快時,消費者會取相同的數據。
這個例子就存在通信:
代碼如下(synchronized+wait-notify模式):
package com.charlesLC_thread;
public class ProduceTest {
public static void main(String[] args) {
Clerk demo = new Clerk();
Customer customer = new Customer(demo);
Producer producer = new Producer(demo);
customer.setName("我是消費者:");
producer.setName("我是生產者:");
producer.start();
customer.start();
}
}
class Clerk{
private int num;
public synchronized void getproduce() {
if (num<20){
notify();
num++;
System.out.println(Thread.currentThread().getName() + "開始生產第"+num+"個產品");
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void costproduce() {
if (num>0){
notify();
System.out.println(Thread.currentThread().getName()+"開始消費第"+num+"個產品");
num--;
}
else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Customer extends Thread{
private Clerk clerk;
public Customer(Clerk clerk ){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("開始消費產品");
while(true){
clerk.costproduce();
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("開始生產產品");
while (true){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.getproduce();
}
}
}
結果如下: