用网页前后台来实现生产者消费者,前台用于显示,后台用于实现算法。

暑假小项目:用网页前后台来实现生产者消费者,前台用于显示,后台用于实现算法。
其中涵盖的技术有 jquery、nodejs、java、mysql
话不多说,直接上学习过程。

step one 生产者消费者模型
并发编程把程序划分为多个独立的子任务,通过多线程来驱动。每个线程有创建、就绪、运行、阻塞、死亡五种状态。wait()方法使线程等待,进入阻塞状态。notify()方法唤醒相应的一个线程,使之进入运行状态。notifyall()方法唤醒所有在等待运行的线程。synchronized是同步关键词,当该词修饰的线程在运行时,不允许其他线程运行。thread()类和runnable()接口都是多线程编程的主要方法,主要区别在于在thread()类中类只能继承一个父类,而在runnable()接口中一个类可以继承多个接口。
而生产者消费者模型实质就是生产者生产商品,并存入缓存仓库,消费者从缓存仓库中消费商品,因此每次生产商品时需要创建一个生产线程,消费时创建一个消费线程,再根据缓存仓库的容量,对生产和消费行为进行一定的约束。

step two 结合数据库mysql
网站运行的持久化需要数据库支持,这里运用了关系型数据库mysql。创建过程不再叙述,数据库主键为turn,表明线程的执行顺序,并且修饰了auto_increment自增关键词(因此在清空数据库数据时应该使用truncate而不是delete)。id表示每个线程的属性(为了弄清楚线程运行的不确定性,笔者给每个线程增加了id属性)。num表示该线程生产或消费商品的数量。sum表示执行完该线程后仓库商品的剩余量。效果如下图:

接下来贴出算法代码,简洁起见,只贴出生产者代码。

class Godown { 
    public static final int max_size = 100; //最大库存量 
    public int curnum;     //当前库存量 
    Godown() { 
    } 
    Godown(int curnum) {
        Connection conn = null;
        try {
            Class.forName("org.gjt.mm.mysql.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("无法加载驱动");
            e.printStackTrace();
        }
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/procons?characterEncoding=utf8&useSSL=false","root","123456");
        } catch (SQLException e) {
            System.out.println("无法连接数据库");
            e.printStackTrace();
        }
        try {
            Statement stmt = conn.createStatement();
            stmt.executeUpdate("insert into storage (id,num,sum) values (0,0," + curnum + ")");
            stmt.close();
            conn.close();
        } catch (SQLException e) {
            System.out.println("无法插入初始化数据");
            e.printStackTrace();
        }
        System.out.println(0 + ".目前库存为:" + curnum);
        this.curnum = curnum;
    }

    public synchronized void produce(int id, int neednum) { 
            //测试是否需要生产 
        while (neednum + curnum > max_size) { 
            System.out.println("要生产的产品数量" + neednum + "超过剩余库存量" + (max_size - curnum) + ",暂时不能执行生产任务!"); 
            try {  
                wait(); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 

        //满足生产条件,则进行生产,这里简单的更改当前库存量 
        curnum += neednum; 
        Connection conn = null;
        try {
            Class.forName("org.gjt.mm.mysql.Driver");
        } catch (ClassNotFoundException e) {
        System.out.println("无法加载驱动");
            e.printStackTrace();
        }
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/procons?characterEncoding=utf8&useSSL=false","root","123456");
        } catch (SQLException e) {
            System.out.println("无法连接数据库");
            e.printStackTrace();
        }
        try {
            Statement stmt = conn.createStatement();
            stmt.executeUpdate("insert into storage(id,num,sum) values("+ id +","+ neednum +","+ curnum +")");
            stmt.close();
            conn.close();
        } catch (SQLException e) {
            System.out.println("无法插入生产数据");
        }
        System.out.println(id + ".已经生产了" + neednum + "个产品,现仓储量为" + curnum);
        //唤醒在此对象监视器上等待的所有线程 
        notifyAll(); 
    }
    //消费
    public synchronized void consume(int id, int neednum) {……} 
}
class Producer extends Thread {
    private int id;
    private int neednum;                //生产产品的数量 
    private Godown godown;            //仓库 

    Producer(int id, int neednum, Godown godown) {
        this.id = id;
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
            //生产指定数量的产品 
            godown.produce(id,neednum); 
    } 
}

step three 写html页面
页面写的比较粗劣,能看就行…

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml-transitional.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <script src="jquery-3.1.0.js" type="text/javascript"></script>
        <script src="jquery.js" type="text/javascript"></script>
    </head>
    <body>
        <p><input type="submit" id="sub" value="显示" /><input type="button" id="but" value="清空"></p>
        <div id="a1"></div>
        <div id="a2"></div>
        <div id="a3"></div>
        <div id="a4"></div>
    </body>
</html>

step four 搭建nodejs服务器
搭建nodejs服务器是最后一步,我的想法是触发网页上click事件,通过使用mysql模块读取后台数据库的数据并显示到网页上。mysql模块比较简单,通过建立一个connection,使用connect()、query()、end()等方法便能使用。而页面与服务器的交互,得到学长的建议,我使用了websocket。websocket是html5的新协议,实现了浏览器和服务器的全双工通信。首先通过已封装好的握手协议,实现浏览器的请求和服务器的回应,涉及http协议。握手成功后有许多操作,比如我写在前端的jquery部分,有onopen、onerror、onmessage(接收服务器传过来的数据)、onclose, node端有on(接收浏览器数据并处理)。
如下为nodejs代码:

var express = require('express');
var mysql = require('mysql');
var ws = require('ws').Server;
var app = express();
var data = new Array();

app.use(express.static(__dirname + '/public'));

app.set('port', process.env.PORT || 3000);

//数据库连接
var connection = mysql.createConnection({
    host:'localhost',
    user:'root',
    password:'123456'
});

var server = new ws({host:"127.0.0.1",port:3000});
server.on('connection',function(ws) {
    console.log('new connection founded successfully');
    //接收数据并处理
    ws.on('message',function(e) {
        if (e==1) {
            connection.connect();
            connection.query('use procons');
            connection.query('select * from storage',function selectCb(err,results,fields){
                if (err) {
                    throw err;
                }
                if (results) {
                    for (var i = 0; i < results.length; i++) {
                        data[i] = results[i].turn+'    '+results[i].id+'  '+results[i].num+'  '+results[i].sum;
                        ws.send(data[i]);
                    }
                }
            });
            connection.end();               
        }
/*      if (e==2) {
            connection.connect();
            connection.query('use procons');
            connection.query('truncate table storage',function(err,result){
                if (err) {
                    throw err;
                }
                if (result) {
                    ws.send(null);
                }
            });
            connection.end();
        }       
*/
    }); 
});
console.log('websocket-server running...');

app.listen(app.get('port'), function(){
  console.log( 'Express started on http://localhost:' + app.get('port') + '; press Ctrl-C to terminate.' );
});

以及jquery文件:

var bool;
var ws = new WebSocket('ws://127.0.0.1:3000');

ws.onopen = function() {
    $("#a1").append("<p>连接建立</p>");    
}

ws.onerror = function() {
    $("#a3").append("<p>连接错误</p>");
}

//接收从服务器发来的数据       
ws.onmessage = function(e) {
    $("#a2").append('<p>'+e.data+'</p>');
}

ws.onclose = function() {
    $("#a4").append("<p>连接关闭</p>");
}

$(function() { 
    $("#sub").click(function() {
        $("#a2").append("<p>turn id num sum</p>");
        bool = 1;
        ws.send(bool);
    });
    $("#but").click(function() {
        $("#a2").remove();
    })
})

网页上的显示结果为:
这里写图片描述

至此,一个简单的前后端交互的生产者消费者模型就完成了。粗劣地完成后,笔者就卸甲归家了,还有很多细节和功能都有待完善,之后再改善了,不过这也是笔者第一次独立完成的小项目,意义非凡。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值