u010774511
最后胜地
采纳率0%
2014-05-13 05:15 阅读 4.4k

多线程编程中,2个线程同时调用一个存储过程…………

最近自学的时候遇到了这么一个问题……
要求是这样的:
1.在数据库中新建一个用户状态表。里面包含2个字段。user_status(userid bigint,status int)
userid为自增。
status默认为0;
插入1000条记录。

2.写一个存储过程,从user_status表中获取一条status=0的userid字段的值,取出userid时,需要同时把这条记录对应的status从0修改为1.

3.开启2个线程,在线程中循环调用第2步中的存储过程,直到user_status表中记录全部取完(即status全部为1)。要求:在2个线程中取出的userid不能重复。并把取出的userid,打印到某个文件中,一行一个userid

第一点,没啥问题吧应该,我在数据库里建好了。

第二点的话,我写的存储过程是:

create procedure sp_getuserid
as
declare @userid int
select top 1 @userid=userid from user_status where status=0 order by newid()
update user_status set status=1 where status=0 and userid =@userid
select userid from user_status where userid=@userid

第三点的代码:

public class Status implements Runnable {
    private int num;
    @Override
    public void run() {
        Boolean flag = true;
        while(flag)
        {
            flag=this.writeFile(this.getUserid());
        }
    }
    @SuppressWarnings("unchecked")
    public List<UserBean> getUserid() {
        // 创建数据库连接
        Connection conn = ConnectDb.Connect();
        QueryRunner qRunner = new QueryRunner();
        List<UserBean> list = new ArrayList<UserBean>();
        try {
            list = qRunner.query(conn, "exec sp_getuserid",
                    new BeanListHandler(UserBean.class));
        } catch (Exception e) {
            e.printStackTrace();
        }
        DbUtils.closeQuietly(conn);
        return list;
    }
    public Boolean writeFile(List<UserBean> list) {
        if (list.size()==0)
            return false;
        else {
            try {
                File file = new File("d:/test.txt");
                if (!file.exists())
                    file.createNewFile();
                FileOutputStream out = new FileOutputStream(file, true);
                for (int i = 0; i < list.size(); i++) {
                    StringBuffer sb = new StringBuffer();
                    SimpleDateFormat df = new SimpleDateFormat(
                            "yyyy-MM-dd HH:mm:ss:SSS");
                    sb.append(df.format(new Date()) + " \t"
                            + list.get(i).getUserid()+ " \t"+this.getNum());
                    sb.append(System.getProperty("line.separator"));
                    out.write(sb.toString().getBytes("utf-8"));
                }
                out.close();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return true;
            }
        }
    }
    public static void main(String[] args) {
        new Thread(new Status(1)).start();
        new Thread(new Status(2)).start();
    }
    public Status(int num)
    {
        this.num=num;
    }

}

可是到最后,我不能在txt里得到1000条数据,总是缺少一些条目,但是数据库里却更新了……

如果我把存储过程改为

create procedure sp_getuserid
as
Begin TransAction
declare @userid int
declare @errno int
set @errno=0 
select top 1 @userid=userid from user_status where status=0 order by newid()
set @errno=@errno+@@error
update user_status set status=1 where status=0 and userid =@userid
set @errno=@errno+@@error
select userid from user_status where userid=@userid
set @errno=@errno+@@error
If @errno>0 
Begin 
rollback TransAction 
end 
Else
Begin 
Commit TransAction
End 

这样运行又会报死锁错误……
好吧,多线程的问题和存储过程不怎么会啊,求指教啊!!

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

3条回答 默认 最新

  • qq_35947177 比特山小鹿 2019-03-08 14:47

    我的这个是java+mysql 第二个问题

    DROP PROCEDURE IF EXISTS sp_status_change1;//删除之前存储过程
    DELIMITER $$

    CREATE PROCEDURE sp_status_change1()
    BEGIN
    declare maxid int;#定义变量最大的id
    declare num int;
    set @uid=1; #当前的id从0开始
    select MAX(userid) INTO maxid from user_status;#当前数据库中最大的id赋值给maxid
    SELECT userid FROM user_status where STATUS =0;
    while @uid<= maxid do #遍历查询,当前uid小于总数时,
    SELECT count(*)INTO num FROM user_status where STATUS =0 and USERID= @uid;
    if 1=num then
    UPDATE user_status
    SET STATUS=1
    where USERID= @uid;
    END IF;
    set @uid=@uid+1;
    end while;
    END$$
    DELIMITER ;

    点赞 1 评论 复制链接分享
  • caodegao 椰树海岛 2014-05-13 06:09

    我们在这样的情况是这样的
    一:在表中新增一个job字段,这个job取序列号;
    二:每次执行存储过程时先获取一个序列,并且写在要操作数据的那个表中;
    三:做完步骤二的更新后用这个job查询出这个序列的数据进行业务;
    四:做完业务后,将job的数据更新为空.

    另外有个重置的行程,更新job在处理时有挂死或者超时的情况,比如你的job不为空,但是status一直还是0.过了一个小时还是这样,就必须重置了
    这样每个job处理的数据全都是自己的数据.希望给你帮助

    点赞 评论 复制链接分享
  • ldh911 MiceRice 2014-05-19 08:10

    主要是存储过程写得不好,没有做事务控制,可以考虑这样:
    1、存错过程:
    在存储过程中使用游标,Select ... For Update,这样游标操作当前行时,可以锁定;然后利用游标来更新字段即可,最后也是返回游标的userid;记得用完了要关闭游标;

    2、单句执行:
    UPDATE user_status SET status=1
    OUTPUT INSERTED.userid
    WHERE userid = (
    Select top 1 userid from user_status where status=0
    )
    要用PrepareStatement执行,注册好返回参数,具体请咨询Google。

    点赞 评论 复制链接分享

相关推荐