问题描述
- 多线程编程中,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
这样运行又会报死锁错误……
好吧,多线程的问题和存储过程不怎么会啊,求指教啊!!
解决方案
我们在这样的情况是这样的
一:在表中新增一个job字段,这个job取序列号;
二:每次执行存储过程时先获取一个序列,并且写在要操作数据的那个表中;
三:做完步骤二的更新后用这个job查询出这个序列的数据进行业务;
四:做完业务后,将job的数据更新为空.
另外有个重置的行程,更新job在处理时有挂死或者超时的情况,比如你的job不为空,但是status一直还是0.过了一个小时还是这样,就必须重置了
这样每个job处理的数据全都是自己的数据.希望给你帮助
解决方案二:
主要是存储过程写得不好,没有做事务控制,可以考虑这样:
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。