线程间进行通信,使得系统之间的交互性更大,大大提高CPU利用率的同时还会使得程序员对各线程任务在处理的过程中进行有效的把控和监督。而最常用的线程间的通信方式主要为两种:操作共享变量和利用管程。本文将总结这两种线程间的通信方式,同时使用wait/notify机制来实现操作系统中的经典同步问题:生产者/消费者问题和对线程的执行次序进行控制。最后介绍ThreadLocal的使用。

1. 操作共享变量

原始方法即采用while语句的轮询机制来检测某一条件,这样会大大的浪费cpu资源。


public void run() {
		try {
			while(true) {
				if(list.size() == 5) {
					System.out.println("==5了,线程b要推出了!");
					throw new InterruptedException;
				}
			}
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
	}

在操作系统中,进程需要访问临界资源(即访问临界区)时,需要遵循的原则为:空闲让进、忙则等待、有限等待、让权等待。因此该操作明显违背了让权等待原则,持续的轮询size变量,占用cpu的资源。

wait/notify机制:执行wait()方法后,当前线程释放锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才释放锁,而呈wait状态所在的线程才可以获取该对象锁。综上:wait使线程停止运行,而notify使停止的线程继续运行。

注:1. 当方法wait()被执行后,锁被自动释放,但执行完notify()方法,锁却不自动释放,必须执行完notify()方法所在的同步synchronized代码块后才释放锁。2. notify()是随机唤醒等待队列中等待同一共享资源的一个线程。

生产者/消费者问题(一对一)
生产者代码


public class P {
	private String lock;
	
	public P(String lock) {
		super();
		this.lock = lock;
		
	}
	
	public void setValue() {
		synchronized(lock) {
			if(!ValueObject.value.equals("")) {
				try {
					lock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			String value = System.currentTimeMillis()+"_"+System.nanoTime();
			System.out.println("set值是"+value);
			lock.notify();
		}
	}
}

消费者代码


public class C {
	private String lock;
	
	public C(String lock) {
		super();
		this.lock = lock;
	}
	
	public void getValue() {
		synchronized(lock) {
			if(ValueObject.value.equals("")) {
				try {
					lock.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("get的值是"+ValueObject.value);
			ValueObject.value = "";
			lock.notify();
		}
	}
}

线程执行次序控制问题


p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #931a68}
p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #4e9072}
p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px}
span.s1 {color: #931a68}
span.s2 {color: #000000}
span.s3 {color: #0326cc}
span.s4 {color: #7e504f}
span.s5 {color: #3933ff}
span.s6 {color: #91afcb}
span.Apple-tab-span {white-space:pre}
public class DBTools {
	volatile private boolean prevIsA = false;
	synchronized public void backupA() {
		try {
			while(prevIsA == true) {
				wait();
			} 
			for(int i = 0; i < 5; i++) {
				System.out.println("11111");
			}
			prevIsA = true;
			notifyAll();
		}catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	synchronized public void backupB() {
		try {
			while(prevIsA == false) {
				wait();
			}
			for(int i = 0; i < 5; i++) {
				System.out.println("22222");
			}
			prevIsA = false;
			notifyAll();
		}catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

利用对prevIsA变量的控制,来控制线程的执行次序,即在操作系统中所使用的互斥变量的机制,来完成线程的以此执行,从而达到控制线程执行的次序

2. 操作管程

Java提供了4个类来使线程间可以通信

1)PipedInputStream和PipedOutputStream 2)PipedReader和PipedWriter

本实例提供基于字节流实现线程间的通信

WriteData.java


public class WriteData {
	public void writeMethod(PipedOutputStream out) {
		System.out.println("write: ");
		for(int i = 0; i < 300; i++) {
			String outData = " " +(i+1);
			try {
				out.write(outData.getBytes());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(outData);
		}
	}
}

ReadData.java


public class ReadData {
	public void readMethod(PipedInputStream input) {
		System.out.println("read :");
		byte[] byteArray = new byte[20];
		try {
			int readLength = input.read(byteArray);
			while(readLength != -1) {
				String newData = new String(byteArray, 0, readLength);
				System.out.println(newData);
				readLength = input.read(byteArray);
			}
			System.out.println();
			input.close();			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}



3. ThreadLocal类

ThreadLocal解决变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal类中进行保存