多線程與併發-線程間通信
概述:
多線程之間通信一般有這樣幾種方法:
- 通過共享對象實現通信
- wait/notify機制
- Condition接口,await/signal機制
- 消息隊列,socket編程等網絡通信
wait/notify機制
基礎
前提:多個線程使用用一把鎖,在使用wait(),notify(),notifyAll()之前要先獲取當前對象的鎖。
- wait():運行至wait處時立刻阻塞當前線程,並釋放鎖。當被通知喚醒後開始競爭鎖,如果獲取到鎖在繼續向下執行。
- notify():運行至notify處時立刻發送通知喚醒一個等待線程,但需要等到同步塊運行結束之後才釋放鎖。
- notifyAll():喚醒所有等待線程。
其餘方法 - wait(long millis),時間到了相當於被通知,自動喚醒
實現
以生產者消費者爲例吧
單一緩存空間
public class A {
private static String value;
class MyService{
private final String lock = new String("");
private int i=0;
public void produce(){
synchronized (lock) {
try {
if(A.value!=null) {
lock.wait();
}
A.value=" value = "+ i++;
System.out.println("生產 :"+A.value);
Thread.sleep(1000);
lock.notify();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void consume(){
synchronized(lock) {
try {
if(A.value==null) {
lock.wait();
}
System.out.println("消費 :"+A.value);
A.value=null;
Thread.sleep(1000);
lock.notify();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class ProduceThread extends Thread{
private MyService service;
ProduceThread(MyService service){
this.service=service;
}
@Override
public void run() {
while(true) {
service.produce();
}
}
}
class ConsumerThread extends Thread{
private MyService service;
ConsumerThread(MyService service){
this.service=service;
}
@Override
public void run() {
while(true) {
service.consume();
}
}
}
public static void main(String[] args) {
A a = new A();
MyService service = a.new MyService();
a.new ProduceThread(service).start();
a.new ConsumerThread(service).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
}
緩存空間大於1
public class A {
private static LinkedList<String> values = new LinkedList<>();
private static final int SIZE = 5;
class MyService{
private final String lock = new String("");
private int i=0;
public void produce(){
synchronized (lock) {
try {
while(A.values.size()==A.SIZE) {
lock.wait();
}
String value =" value = "+ i++;
A.values.add(value);
long id = Thread.currentThread().getId();
System.out.printf("Thread %d 生產 :%s\n",id,value);
lock.notifyAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void consume(){
synchronized(lock) {
try {
while(A.values.size()==0) {
lock.wait();
}
String value = A.values.poll();
long id = Thread.currentThread().getId();
System.out.printf("Thread %d 消費 :%s\n",id,value);
lock.notifyAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ProduceThread extends Thread{
private MyService service;
ProduceThread(MyService service){
this.service=service;
}
@Override
public void run() {
while(true) {
service.produce();
}
}
}
class ConsumerThread extends Thread{
private MyService service;
ConsumerThread(MyService service){
this.service=service;
}
@Override
public void run() {
while(true) {
service.consume();
}
}
}
public static void main(String[] args) {
A a = new A();
MyService service = a.new MyService();
List<Thread> threads = new ArrayList<>();
for(int i=0;i<10;i++) {
threads.add(a.new ProduceThread(service));
threads.add(a.new ConsumerThread(service));
}
for(Thread thread : threads) {
thread.start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
}
爲不讓單一線程完成所有生產或消費在外面休眠一定時間。
使用while與notifyAll避免假死現象出現(所有線程處於等待狀態),原因:notify只通知一個線程,可能是同類型線程而非異構線程(生產者->消費者,消費者->生產者)
同時也可以通過while避免等待條件發生改變,避免過早通知問題
Condition
await/signal機制
await()與wait()類似
signal()與notify類似,但通知的是同一個Condition
一個Lock對象可以創建多個Condition條件。實現部分通知功能。