章前准备 做为程序猿的骄傲,不懂多线程是不能的,先来几个多线程的使用方法
呆毛:五个小狗和一个饭盒的故事,其中
Dog拥有eat方法不断循环访问Box,当Box.val>0,小狗会将Box.val=0,并打印
Box拥有put方法不断修改val的值
当然每个小狗可以有他自己的饭盒,但是做为优秀的程序猿(万恶的资本家),能用一个饭盒就用一个,干嘛用5个累
简单实现一:尝试使用线程完成呆毛,并观察
/** * * @author 程猿 * 为了便于观察,小狗将间隔0.5s去查看饭盒 * 简单的实现了小狗与饭盒的共享关系 */ public class Dog implements Runnable{ public static void main(String[] args) { Box box=new Box(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); box.put(); } Box box; public Dog(Box box) { this.box=box; } void eat(){ while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在搜索饭盒"); if(box.val!=0d){ System.out.println(Thread.currentThread().getName()+"搜索饭盒,获得了"+box.val); box.val=0d; } else{ System.out.println("没有食物"+Thread.currentThread().getName()+"失望的离开...."); } } } @Override public void run() { eat(); } }
/** * @author 程猿 * 每隔1s将饭盒填满 */ public class Box{ double val=0d; void put(){ while(true){ try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } this.val=Math.random(); System.out.println("放满了食物"+this.val); } } }
运行结果:
Thread-4正在搜索饭盒 Thread-2正在搜索饭盒 Thread-3正在搜索饭盒 Thread-1正在搜索饭盒 Thread-0正在搜索饭盒 没有食物Thread-1失望的离开.... 没有食物Thread-3失望的离开.... 没有食物Thread-2失望的离开.... 没有食物Thread-4失望的离开.... 没有食物Thread-0失望的离开.... Thread-1正在搜索饭盒 Thread-3正在搜索饭盒 Thread-2正在搜索饭盒 Thread-0正在搜索饭盒 Thread-4正在搜索饭盒 Thread-2搜索饭盒,获得了0.8894110314731649 Thread-1搜索饭盒,获得了0.8894110314731649 Thread-0搜索饭盒,获得了0.8894110314731649 Thread-4搜索饭盒,获得了0.8894110314731649 Thread-3搜索饭盒,获得了0.8894110314731649
从结果上看,这段代码就像文字游戏一样不断地运行,但是小狗们好像发生了抢饭盒事件....虽说一起吃时间很温馨的事,但我们需要的模拟的是小饭盒,只容纳一个小狗,换一个专业的说法叫事物,稍微了解一下线程就知道,synchronized获取唯一支配的权利,或许可以帮助我们让小狗变得井然有序
简单实验二:加入synchronized
/** * @author 程猿 小狗在执行可以正真的进行吃时synchronized(box) 对于box中放入的方法,暂时不处理 */ public class Dog implements Runnable { public static void main(String[] args) { Box box = new Box(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); box.put(); } Box box; public Dog(Box box) { this.box = box; } void eat() { while (true) { synchronized (box) { // System.out.println(Thread.currentThread().getName()+"正在搜索饭盒"); if (box.val != 0d) { System.out.println(Thread.currentThread().getName() + "搜索饭盒,获得了" + box.val); box.val = 0d; } else { // System.out.println("没有食物" // + Thread.currentThread().getName() + "失望的离开...."); } } } } @Override public void run() { eat(); } }
运行结果:
放满了食物0.6205712078747138 Thread-0搜索饭盒,获得了0.6205712078747138 放满了食物0.481413397240385 Thread-2搜索饭盒,获得了0.481413397240385 放满了食物0.9420938712813596 Thread-0搜索饭盒,获得了0.9420938712813596 放满了食物0.16206165621621393 Thread-3搜索饭盒,获得了0.16206165621621393
井然有序,除了这个词还有什么能表达我的赞美~当然为了有效的观察结果,我专门注释了好多System.out.println,文字游戏看起来不怎么完善,但是无所谓啦....但是仔细的想想,每次都让小狗没有策略的去寻找饭盒显然是不人道的(资本家的人道),就算是做为模拟测试,也会比较费电(电脑都开始叫了),如果没有食物,小狗就不能关注饭盒,然后回家睡觉,等饭盒里有饭了,再回来看嘛,这种通知的设计模式,不就是观察者嘛,即使做为BI端的屌丝程序猿也可以试着完成吧...
遗憾的是JAVA大叔已经对多线程提供了类似的解决方案,show 设计模式的机会有木有了╮(╯▽╰)╭
简单实现三:加入等待与通知,达到劳逸结合效果(省电效果)
public class Dog implements Runnable{ public static void main(String[] args) { Box box=new Box(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); box.put(); } Box box; public Dog(Box box) { this.box=box; } void eat(){ while(true){ synchronized (box) { if(box.val!=0d){ System.out.println(Thread.currentThread().getName()+"搜索饭盒,获得了"+box.val); box.val=0d; }else{ try { System.out.println(Thread.currentThread().getName()+"搜索饭盒,什么也没得到,他生气的睡着了"); box.wait(); System.out.println(Thread.currentThread().getName()+"好像饭盒满了"); } catch (InterruptedException e) { e.printStackTrace(); } } } } } @Override public void run() { eat(); } }
public class Box{ double val=0d; void put(){ while(true){ try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } synchronized (this) { this.val=Math.random(); System.out.println("放满了食物"+this.val); this.notifyAll(); // this.notify(); } } } }
运行效果:
Thread-0搜索饭盒,什么也没得到,他生气的睡着了 Thread-3搜索饭盒,什么也没得到,他生气的睡着了 Thread-2搜索饭盒,什么也没得到,他生气的睡着了 Thread-1搜索饭盒,什么也没得到,他生气的睡着了 Thread-4搜索饭盒,什么也没得到,他生气的睡着了 放满了食物0.7126497780788127 Thread-4好像饭盒满了 Thread-4搜索饭盒,获得了0.7126497780788127 Thread-4搜索饭盒,什么也没得到,他生气的睡着了 Thread-1好像饭盒满了 Thread-1搜索饭盒,什么也没得到,他生气的睡着了 Thread-2好像饭盒满了
这次可是真睡,至少电脑没叫吧...当然如果你觉得小狗占有的box时间有点长(synchronized内的代码),你可以尝试着分割看看,也可以使用ifif而不是ifelse,也可以尝试着使用 this.notify();对理解很有帮助哦
总之,看起来小狗都没有问题了,现在开始找饭盒的事了...可以看到饭盒是在主线程上运行的,简单的说,我们的主线程将会一直干一个盛饭工的工作,他就像吃了炫迈一样,根本停不下来....我们需要在控制台(主线程)做为调度的控制台(领导),由他来指挥,控制别的线程才对
简单实现四:饭盒也是一个线程(雇一个打饭工)
/** * * @author 程猿 * 由控制台来决定是否通知小狗 */ public class Dog implements Runnable{ Box box; public Dog() { } public Dog(Box box) { super(); this.box = box; } public static void main(String[] args) { Box box=new Box(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); new Thread(new Dog(box)).start(); PutFoodThread putFoodThread = new PutFoodThread(box); new Thread(putFoodThread).start(); while(true){ double food = putFoodThread.getFood(); box.put(food); } } void eat(){ while(true){ synchronized (box) { if(box.val==0d){ try { box.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ System.out.println(Thread.currentThread().getName()+"得到"+box.val); box.val=0d; } } } } @Override public void run() { eat(); } }
public class Box{ double val; public Box() { super(); } public Box(double val) { super(); this.val = val; } public void put(double val) { synchronized (this) { this.val = val; this.notifyAll(); } } }
/** * @author 程猿 * 只要打饭工(PutFoodThread)在,控制台可以通过getFood获取新鲜的食物 */ public class PutFoodThread implements Runnable{ Box box; boolean flag=false; public PutFoodThread(Box box) { super(); this.box = box; } @Override public void run() { while(true){ try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } synchronized (box) { this.box.val=Math.random(); // this.box.put(Math.random()); box.notifyAll(); flag=true; System.out.println("放满了食物"); } } } public double getFood(){ if(flag){ flag=false; return box.val; } synchronized (box) { try { box.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } flag=false; return box.val; } }
运行结果:
放满了食物 Thread-4得到0.8640579008568935 放满了食物 Thread-0得到0.009200825436603832 放满了食物 Thread-0得到0.930894934537914 放满了食物 Thread-0得到0.47626365479208954 放满了食物
通过代码可以发现,因为要求由控制台来决定是否往饭盒里放饭,其实控制台依然无法给那只小狗喂饭,而且打饭工并不需要饭盒,可以将饭盒改为new Object(),或者直接就由打饭工来通知所有小狗....当然到这里回想一下最开始的,为什么用一个饭盒?我连打饭工都请了,加四个饭盒能照成多大危机?我们由打饭工生产饭,控制台决定给那只小狗喂饭,小狗则吃了睡,睡了吃~
简单实现五:共享的只是饭
/** * * @author 程猿 一只普通的饭盒 */ class Box { double val; public Box() { super(); } public Box(double val) { super(); this.val = val; } } /** * * @author 程猿 为了让控制台能够选择小狗,需要一个小狗的名单(容器) */ public class Dog implements Runnable { Box box = new Box(); public Dog() { } public Dog(Box box) { super(); this.box = box; } public void setFood(double val) { synchronized (this.box) { this.box.val = val; this.box.notify(); } } public static void main(String[] args) { List pool = new ArrayList(); startDog(pool); startDog(pool); startDog(pool); startDog(pool); startDog(pool); PutFoodThread putFoodThread = new PutFoodThread(); new Thread(putFoodThread).start(); while (true) { double food = putFoodThread.getFood(); getDogFromPool(pool).setFood(food); } } public static void startDog(List pool) { Dog dog = new Dog(); pool.add(dog); new Thread(dog).start(); } /** * * @param pool * 小狗名单 * @return 随机获取小狗 */ public static Dog getDogFromPool(List<Dog> pool) { return pool.get((int) (Math.random() * 5)); } void eat() { while (true) { synchronized (box) { if (box.val == 0d) { try { box.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName()+"得到"+box.val); box.val = 0d; } } } } @Override public void run() { eat(); } }
public class PutFoodThread implements Runnable{ double val; Object obj=new Object(); boolean flag=false; void put(){ while(true){ try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } this.val=Math.random(); synchronized (obj) { obj.notify(); } flag=true; System.out.println("放满了食物"); } } @Override public void run() { put(); } public double getFood(){ if(flag){ flag=false; return val; } synchronized (obj) { try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } flag=false; return val; } }
运行结果:
放满了食物 Thread-1得到0.8098550772264681 放满了食物 Thread-4得到0.42056905509104414 放满了食物 Thread-2得到0.5435716007168193 放满了食物 Thread-4得到0.7833656323252407
至此总结一下,实现四与实现五都能够完成任务,相对于四的线程粗放的管理,五对线程的把控,绝对是有突破性意义的(就是线程池了)....其实这就是生产者消费者,为毛我总觉得这是多线程的应用...
线程这东西,远比这里说的复杂的多,关于线程为什么需要synchronized为什么需要对象,以及wait/notify为什么需要synchronized,此事与java内存结构有关,或者说具体的字节(.class)包括线程池什么时候应该使用....管他呢,以后遇见再说吧
第三章 连接器
1.tomcat大叔对servlet解决方案创建了一套模型,整套模型分两个模块,以目前来看
连接器(connector):创建req与res
容器(container):接受req与res,并调用相应的servlet
2.http协议(req与res)的简单实现,如果对url中的参数(如jsessionid)有疑问的话,在这里或许能得到答案
这一章最重要的是建立了基本的模型,而后对每个模型进行更细致化的分类
第四章 Tomcat的默认连接器
1.支持http1.1协议
2.使用多线程(线程池)优化方案