3.4.连接器--生产者消费者

章前准备  做为程序猿的骄傲,不懂多线程是不能的,先来几个多线程的使用方法


   呆毛:五个小狗和一个饭盒的故事,其中
  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)包括线程池什么时候应该使用....管他呢,以后遇见再说吧

tomcat_3.线程.zip

第三章 连接器

1.tomcat大叔对servlet解决方案创建了一套模型,整套模型分两个模块,以目前来看
连接器(connector):创建req与res
容器(container):接受req与res,并调用相应的servlet
2.http协议(req与res)的简单实现,如果对url中的参数(如jsessionid)有疑问的话,在这里或许能得到答案
这一章最重要的是建立了基本的模型,而后对每个模型进行更细致化的分类

第四章 Tomcat的默认连接器

1.支持http1.1协议
2.使用多线程(线程池)优化方案

原文地址:https://www.cnblogs.com/liuCy/p/4062727.html