首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > Java相关 >

多线程并发处理,该怎么解决

2013-01-23 
多线程并发处理数据库里有两个表A,表B,结构一样。需要做的是把表A的数据拿出来,处理后放到表B。现在我是用线

多线程并发处理
数据库里有两个表A,表B,结构一样。
需要做的是把表A的数据拿出来,处理后放到表B。
现在我是用线程取,每10秒调用一次存储过程。
每次取100条数据。
存储过程如下:


UPDATE t_tableA SET PushStatus =0 WHERE PushStatus=-1 limit 100
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
INSERT INTO t_tableB SELECT * FROM t_tableA WHERE PushStatus = 0;
DELETE FROM t_tableA WHERE PushStatus=0;

然后线程如下

package cn;

public class Thread implements Runnable{

private String sendUrl ;
private int milliSecond ;

public SmsSendThread(String sendUrl, String milliSecond) {
this.sendUrl=sendUrl;
this.milliSecond=Integer.parseInt(milliSecond);
}

public void run() {
while(true){
try {
Thread.sleep(milliSecond);
Service.getInstance().send(sendUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

在Service类的send方法里调用了存储过程取数据,处理数据,并返回值后按照ID进行处理成功的标识更改。
我首先起了一个线程调用三次 如下

Thread sst = new Thread(sendUrl,milliSecond);
new Thread(sst).start();
new Thread(sst).start();
new Thread(sst).start();


速度增加了,但是肯定会有重复的数据。

怎么才能让在没有重复数据的情况下增加处理的速度呢?

如没明白可提问我再补充 多线程
[解决办法]
引用:


UPDATE t_tableA SET PushStatus =0 WHERE PushStatus=-1 limit 100
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
INSERT INTO t_tableB SELECT * FROM t_tableA WHERE PushStatus = 0;
DELETE FROM t_tableA WHERE PushStatus=0;

先分析一下,如果像楼主那样处理,会发生什么样的冲突:
1、对于update来说,不同线程间的update不会有影响,因为数据库会处理这个冲突。
2、对于select来说,如果在delete前又有线程update,就会出现重复的数据,这样造成重复的处理;
3、对于insert来说,如果在delete前又有线程insert,就会造成重复的数据插入t_tableB表。
4、对于delete来说,如果一个线程刚update完,还没来得及select和insert,另一个线程就delete了,那么数据就会丢失。
……这里的冲突很多,就不一一列举了。

首先提个建议,楼主之前分别用PushStatus=-1表示未处理,PushStatus=0表示处理中/完毕。
可以再引入一个状态来区分处理中和处理完毕。如PushStatus=0表示处理中,PushStatus=2表示处理完毕。
------------------------------------------

宗旨:减少冲突,减少阻塞,提高并发,尽量批量操作。

方案1-模仿CAS的无阻塞算法:     【缩小更新的粒度】
1、先找出符合要求的100条;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus=-1 limit 100
2、处理每条数据,处理前先更新,更新成功后再处理。(这样能避免数据的重复处理,把同步的任务交给了数据库)
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 AND ID=:ID
3、对该条数据进行insert和delete,此处的修改都是按照ID的,所以不会造成冲突。
点评:没有使用锁,线程间不用阻塞等待。但是每条数据都要分开更新,如果每条数据处理时间很短的话,那么效率就会大打折扣。

方案2-读取锁
1、更新-读取-更新:先更新是为了批量获取,获取后再更新是为了批量占用已选数据。
把这个过程锁起来,保证每次读的时候不会冲突,这样就不会产生重复处理的数据了。
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 limit 100;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
UPDATE t_tableA SET PushStatus=2 WHERE PushStatus=0;
点评:在读取的时候加锁,读取和占用更新都是批量的,既解决了数据冲突的问题,也大大减少数据库的操作次数。
但是,由于加了读取锁,所以要尽量增大每次读取的数量,减少读取的次数,否则锁竞争会影响效率。

方案3-分角色线程
这个场景中,有四个操作:读取数据、处理数据、转移数据、清理数据。
所以我们可以分成三种不同的线程来处理:
1、读取线程Read:负责读取数据,update和select操作,先update成处理中状态PushStatus=0,select获取数据,然后分配给数据处理线程Deal来做。
2、数据处理线程Deal:负责处理数据,处理完毕后update成完成状态PushStatus=2,因为数据都是读取线程Read传过来的,由读取线程统一管理,所以数据处理线程只管处理,不用考虑数据的冲突,真正实现并发操作。
3、清理线程Clear:负责把已完成PushStatus=2的数据转移到B表,然后清理A表。【清理的时候也涉及到数据冲突的问题,类似上面的情况,就不再讨论了】
点评:这三种线程角色相互独立,可以并行;而同一种线程角色中又可以并发,实现横向和纵向的并发。
但是结构相对比较复杂,适合规模大的负责的业务场景。
理想状态:
读取线程Read获取数据后调用数据处理线程Deal,在Deal处理过程中,Read继续获取新的数据,等Deal处理完毕了,Read刚好获取完新的数据,然后又传到Deal中来处理。【两者线程数量的比例根据各自处理时间来定】
另外,如果B表的实时性不高的话,清理线程Clear一个就够了,让他每隔一段时间扫一扫就可以了。


楼主也可以结合不同方案,组合成一个新的构想!多线程并发处理,该怎么解决
[解决办法]
你可以先把记录分页,然后一个线程去处理一页的数据。

热点排行