public class ThreadDemo6 { public static void main(String[] args) { PipedOutputStream pos=null; PipedInputStream pis=null; try { pos=new PipedOutputStream(); pis=new PipedInputStream(pos); } catch (IOException e) { e.printStackTrace(); } Sender sender=new Sender(pos); Thread t1=new Thread(sender); Receiver receiver=new Receiver(pis); Thread t2=new Thread(receiver); t1.start(); t2.start(); } } class Sender implements Runnable { private OutputStream out; public Sender(OutputStream out) { super(); this.out = out; } @Override public void run() { for (int i = 1; i <= 5; i++) { byte value = (byte) (Math.random() * 100); System.out.println("send value is " + value); try { out.write(value); } catch (IOException e) { e.printStackTrace(); } } } } class Receiver implements Runnable { private InputStream in; public Receiver(InputStream in) { super(); this.in = in; } @Override public void run() { for (int i = 1; i <= 5; i++) { try { byte value = (byte) in.read(); System.out.println("receiver value is " + value); } catch (IOException e) { e.printStackTrace(); } } } }
有一个弊端就是在所有的数据写完后才能去读取,这样在写的时候一直占用内存,我们希望每写一点就能拿一点。看下边的实现:
线程直接的通信:使用Thread.yield()模拟一个生产者和消费者的模型
* 效率还是比较差。yield方法是主动放弃了cpu,又去竞争。
public class ThreadDemo7 { public static void main(String[] args) { FlagSend flagSend = new FlagSend(); FlagRec flagRec = new FlagRec(flagSend); Thread t1 = new Thread(flagSend); Thread t2 = new Thread(flagRec); t1.start(); t2.start(); } } class FlagSend implements Runnable { int theValue; boolean flag;//是否有食物:true有食物 @Override public void run() { for (int i = 1; i <= 5; i++) { while (flag) { Thread.yield(); } theValue = new Random().nextInt(1000);// 制造食物 System.out.println("send value is " + theValue); flag = true;// 等待食客去吃 } } } class FlagRec implements Runnable { public FlagRec(FlagSend flagSend) { super(); this.flagSend = flagSend; } private FlagSend flagSend; @Override public void run() { for (int i = 1; i <= 5; i++) { while (!flagSend.flag) { Thread.yield(); } System.out.println("receiver value is " + flagSend.theValue); flagSend.flag = false; } } }
yield方法是主动放弃了cpu,又去竞争。效率比较差,接收的人不知道是否send已经Ok了。我们希望我们发送以后就去提醒接收者去拿不是一直去在放弃。
线程直接的通信:wait/notify 任何一个对象都拥有一个线程等待池
* 挂在同一个对象的线程等待池中的线程之间可以互相唤醒 wait/notify是属于Object类的
* wait方法必须放入synchronized同步块中
/** * @author Janle * */ public class ThreadDemo8 { public static void main(String[] args) { Cooker cooker=new Cooker(); Diners diners=new Diners(cooker); Thread t1=new Thread(cooker); Thread t2=new Thread(diners); //当程序只有守护线程时候程序退出。 t2.setDaemon(true); t1.start(); t2.start(); } } class Cooker implements Runnable { boolean hasFood; int value; @Override public void run() { for (int i = 1; i <= 5; i++) { // 将锁挂到同一个线程等待池中 synchronized (this) { try { while (hasFood) {// 使用while是因为存在虚假唤醒,不能使用if /** * 在没有被通知、中断或超时的情况下,线程还可以唤醒一个所谓的虚假唤醒 (spurious wakeup)。 * 虽然这种情况在实践中很少发生,但是应用程序必须通过以下方式防止其发生, * 即对应该导致该线程被提醒的条件进行测试, 如果不满足该条件,则继续等待。换句话说,等待应总是发生在循环中 */ this.wait();//wait方法会释放钥匙 } } catch (InterruptedException e) { e.printStackTrace(); } value = new Random().nextInt(1000); System.out.println("Cooker 生产食物 " + value); hasFood=true; this.notify(); } } } } class Diners implements Runnable { private Cooker cooker; public Diners(Cooker cooker) { super(); this.cooker = cooker; } @Override public void run() { for (;;) { // 将锁挂到同一个线程等待池中 synchronized (cooker) { try { while(!cooker.hasFood){ cooker.wait(); } } catch (Exception e) { e.printStackTrace(); } System.out.println("Diners 消费食物 " + cooker.value); cooker.hasFood=false; cooker.notify(); } } } }
线程中的通讯:在实际的业务中的使用
public class ThreadDemo9 { public static void main(String[] args) { final Bussiness bus=new Bussiness(); Thread t1=new Thread(new Runnable() { @Override public void run() { bus.send(); } }); Thread t2=new Thread(new Runnable() { @Override public void run() { bus.recived(); } });t2.setDaemon(true); t1.start(); t2.start(); } } class Bussiness { boolean flag; int value; public void send() { for (int i = 0; i < 5; i++) { synchronized (this) { try { while (flag) { this.wait(); } } catch (Exception e) { e.printStackTrace(); } value = new Random().nextInt(1000); System.out.println("send value is " + value); flag = true; this.notify(); } } } public void recived() { while (true) { synchronized (this) { try { while (!flag) { this.wait(); } } catch (Exception e) { e.printStackTrace(); } System.out.println("receive value is " + value); flag = false; this.notify(); } } } }
一个在处理缓存时候超级实用的例子
/* * @author vayne * * 多线程实现缓存的小demo */ class Cachend { volatile Map<String, String> cachmap = new HashMap<String, String>();//加volatile关键字保证可见性。 ReadWriteLock rwLock = new ReentrantReadWriteLock();//这个读写锁要定义在方法外面,使得每一个线程用的是同一个读写锁。 public String getS(String key) //如果定义在方法内部,就是跟方法栈有关的读写锁。这样可能不是同一个锁。 { rwLock.readLock().lock(); String value = null; try { value = cachmap.get(key); if (cachmap.get(key) == null)//这里要重新获得key对应的value值 { rwLock.readLock().unlock(); rwLock.writeLock().lock(); try { if (cachmap.get(key) == null)//这里也是 { value = "" + Thread.currentThread().getName(); cachmap.put(key, value); System.out.println(Thread.currentThread().getName() + " put the value ::::" + value); } } finally { rwLock.readLock().lock(); //将锁降级,这里跟下一句的顺序不能反。 rwLock.writeLock().unlock();//关于这里的顺序问题,下面我会提到。 } } } finally { rwLock.readLock().unlock(); } return cachmap.get(key); } }