线程间的协作关系与线程同步

[size=x-large]1.线程间的协作关系[/size]
当一个进程中的多个线程为完成同一任务而分工协作时,它们彼此之间有联系,[color=red]知道其他线程的存在,而且受其他线程执行的影响[/color]。这些线程间存在协作关系,这是线程间的直接制约关系。由于合作的每一个线程都是独立地以不可预知的速度推进,这就需要相互协作的线程在某些协调点上协调各自的工作。当合作线程中的一个到达协调点后,在尚未得到其伙伴线程发来的信号之前应阻塞自己,直到其他合作线程发来协调信号后方被唤醒并继续执行。[color=red]这种协作线程之间相互等待对方消息或信号的协调关系称为线程同步。[/color]
例如:发送线程和接收线程。
本例演示传送数据的两个线程之间存在的协作关系,演示在没有实现线程同步运行时存在的错误。
设计两个线程类:一个发送线程类Sender和一个接收线程类Receiver。两个线程对象并不能直接发送数据,它们必须约定一个存放数据的地方,通常称为缓冲区。所以,还要声明一个缓冲区类Buffer,缓冲区有一个成员变量value记录值,并提供对成员变量进行读/写操作的方法,发送线程调用put()方法设置value值,接收线程调用get()方法获得value值,如图所示:

[img]http://dl2.iteye.com/upload/attachment/0130/3624/95db1053-0a79-39ab-82dc-082b29000ea0.png[/img]

package com.jbx.xiezuo;

public class Buffer {
private int value; // 共享变量

public int get() {
return value;
}

public void put(int i) {
this.value = i;
}
public static void main(String[] args) {
Buffer buffer = new Buffer();
(new Sender(buffer)).start();
(new Receiver(buffer)).start();
}
}

class Sender extends Thread { // 发送线程类
private Buffer buffer; // 用于交换数据的共享变量

public Sender(Buffer buffer) { // 指定缓冲区
this.buffer = buffer;
}

public void run() {
for (int i = 1; i < 6; i++) { // 连续向缓冲区发送若干数据
buffer.put(i);
System.out.println("Sender put: " + i);
try {
sleep(1);
} catch (InterruptedException e) {

}
}
}
}

class Receiver extends Thread { // 接收线程类
private Buffer buffer;

public Receiver(Buffer buffer) {
this.buffer = buffer;
}

public void run() {
for (int i = 1; i < 6; i++) { // 连续从缓冲区接收若干数据
System.out.println("\t\t Receiver get:" + buffer.get());
try {
sleep(1);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}

}


某种运行结果:
Sender put: 1
Receiver get:1
Sender put: 2
Receiver get:2
Sender put: 3
Receiver get:3
Receiver get:4
Sender put: 4
Sender put: 5
Receiver get:5

解析:虽然发送线程与接收线程每次休息的时间一样,但由于线程调度的不确定性,两个线程只是随机地交替执行,并不是步调一致地同步运行。此时,出现错误的原因并不是因为并发线程共享了缓冲区,而是因为它们访问缓冲区的速率不匹配。
这两个线程必须协调一致地运行,每做一个操作,双方都要互通消息,发送线程必须在确定接收线程已接收到数据后,才能再次发送;接收线程每接收一个数据都要通知发送线程。这样才能确保数据传送的正确性。因此,协作线程间必须同步进行。
[size=x-large]
2.线程同步[/size]
线程同步是解决线程间协作关系的手段。线程同步是指两个以上线程基于某个条件来协调它们的活动。一个线程的执行依赖于另一个线程线程的信号,当一个线程没有得到来自于另一个线程的信号时则需等待,直到信号到达才被唤醒。
前述的线程互斥关系是一种特殊的线程同步关系,即逐次使用互斥共享资源,也是对线程使用资源次序上的一种协调。
[size=x-large]3.线程同步机制[/size]
操作系统中实现线程同步有一种工具称为信号量和PV操作,它的指导思想源于采用多种颜色信号灯管理交通的方法,描述如下:
(1)背景
多个线程需要对同一个共享变量进行操作,所以多个线程间必须互斥地执行,即这些操作方法必须是互斥的。
(2)设置信号量
为这个共享变量约定一个信号量(semaphore),设置信号量有多种状态,就像交通信号灯有多种颜色一样。信号量状态的设置有多种方式,既可以有两种状态,也可以有多种状态。两种状态用一个布尔值即可表示,true表示可执行,false表示不可执行,就像“红灯停,绿灯行”。多种状态表示一种轮流执行方式,如n为1时,约定线程1可执行;n为2时,约定线程2可执行,等等。
测试信号量状态的操作称为P操作,改变信号量状态的操作称为V操作,这两种操作互斥执行的,并且执行时不能被打断。
(3)线程根据信号量状态而执行
多个线程间彼此根据信号量的状态确定该谁执行,当一个线程开始执行时,它先要测试信号量的状态,如果状态合适,则执行,进行相关操作并更改信号量状态,唤醒其他等待线程执行;否则等待,使线程自己处于阻塞状态,直到被唤醒再次执行。
这样,交互的并发线程之间通过交换信号来达到调整相互速率,保证线程协调运行的目的。利用信号量和P、V操作既可以解决并发进程的竞争问题,又可以解决并发进程的协作问题。

4.Java的线程通信方法
java.lang.Object 类提供wait()、notify()和notifyAll()方法实现线程间通信,方法声明如下:
public final void wait() throws InterruptedException //等待
public final native void wait(long timeout) throws InterruptedException;//等待指定时间
public final native void notify();//唤醒一个等待线程
public final native void notifyAll();//唤醒所有等待线程
这些方法可以被任意类的对象调用,并且声明为最终方法,不能被子类覆盖。一个对象调用wait()方法使当前执行线程变为等待状态,直到其他线程调用notify(),直到其他线程调用notify()或notifyAll()方法通知当前线程才停止等待,该线程被唤醒。
wait(),notify()和notifyAll()方法提供线程间通信方法,对于线程同步问题,仅有这三个方法是不够的,还必须设置信号量及状态,约定对于共享变量的多种互斥操作方法。

你可能感兴趣的:(多线程)