我有一个函数,提供给客户调用
这个函数内容是发短信,然后扣除客户对应的费用,取客户当前余额减发送数.问题出来了:
客户写了多线程的程序调用这个函数发短信,取客户当前余额经常被取重复.
请问,我要怎么样改我的这个函数才能实现线程同步!(我这样描叙不知道大家能不能看懂,如果需要进一步说明请留言在后面)
一定结帖!
这个函数内容是发短信,然后扣除客户对应的费用,取客户当前余额减发送数.问题出来了:
客户写了多线程的程序调用这个函数发短信,取客户当前余额经常被取重复.
请问,我要怎么样改我的这个函数才能实现线程同步!(我这样描叙不知道大家能不能看懂,如果需要进一步说明请留言在后面)
一定结帖!
{
if (无效短信) return -1;
发送;
余额=余额-发送条数;
return 1;}我这样加了synchronized ,但今天测下来问题还是有之前的问题。
{ synchronized (余额) {
if (无效短信) return -1;
发送;
余额=余额-发送条数;
return 1;
}
}
现在看来发送和扣费还是有问题,我再查查日志,看问题出在哪??
public int sendMessage(String userid,String msgText)
{
if (无效短信) return -1;
synchronized (users) {
发送;
余额=余额-发送条数;
log.info("余额-发送条数"+余额+"-"+发送条数)
return 1;
}
}
在user里加一个方法synchronized calcMoney_remain(){
余额=余额-发送条数;
}
当然getMoney_remain()也要同步。
这样就不用再sendMessage()里计算余额了,而是让user类负责计算自己的余额。
sendMessage()也就不需要同步了。
2008-10-21 09:27:46,468 [service.BusService]-[INFO] |Modify users
2008-10-21 09:27:46,476 [service.BusService]-[INFO] |余额-发送条数184-1 《--问题在这里
2008-10-21 09:27:46,501 [service.BusService]-[INFO] |余额-发送条数184-1 《--问题在这里我不知道
public int sendMessage(String userid,String msgText)
{
if (无效短信) return -1;
synchronized (users) {
发送;
余额=余额-发送条数;
log.info("余额-发送条数"+余额+"-"+发送条数)
return 1;
}
}
为什么实际上users这个资源为什么没有被同步,还出现了线程问题。
有没有可能是sendMessage()每次都是根据userid来new一个user?
那synchronized (users) 就没用了。
public int sendMessage(String account, String password, String destmobile,
String msgText) { log.info("Begin sendMessage");
int taskID = Utils.getTaskID();
try {
SimpleDateFormat dateformat = new SimpleDateFormat(
Constants.DATE_TIME_FORMAT);
int userid = users.getUser_id();
double price = getPrice(account);
for (int i = 0; i < msgCount; i++) {
String msg = msgText.substring(0, Math.min(maxChars, msgText
.length()));
String beginSendDate = dateformat.format(new Date());
Users users = daoService.getUserByAccount(account);
synchronized (users){
String ispTaskId = adapte.sendMessage(destmobile, msg);
boolean successSend = true;
try
{
long rt = Long.parseLong(ispTaskId.substring(0, Math.min(5, ispTaskId.length())));
log.info("|rt" + rt);
if (rt < 0) {
successSend = false;
}
}catch (Exception ex) {
log.info("Exception ex");
successSend = false;
}
if (!successSend)
{
return -3;
} String endSendDate = dateformat.format(new Date());
if (msgText.length() > maxChars) {
msgText = msgText.substring(maxChars, msgText.length());
}
} log.info("|Modify users");
users = daoService.getUserByAccount(account);
log.info("|remain_money="+users.getMoney_remain()+"-"+msgCount);
users.setMoney_remain(users.getMoney_remain() - msgCount); //这里是改费用
daoService.modifyUser(users);
}
} catch (Exception ex) {
log.info("|SendMessage error: " + ex);
if (ex instanceof java.lang.IndexOutOfBoundsException)
return -88;
else
return -9;
}
return taskID;
}
看样子问题在这里了怎么改,给个方案,哥们!!
HashMap<String, Object> userLockTable = new HashMap<String, Object>();
synchronized (userLockTable) {
if (!userLockTable.containsKey(account)) {
userLockTable.put(account, new Object());
}
}
Object lock = userLockTable.get(account);
synchronized (lock) {
//...
}
当然还有一些细节要考虑,比如是否要定时clear这个表免得它越来越大。
总之就是为每个userid分配锁,而不是对于user对象。
用单态可以么?
Users users = daoService.getUserByAccount(account);
如果你可以做到一个id只有一个user,那用原来的synchronized (users) 应该是可以正确同步的。
否则的话,可以用我15楼的方法.用synchronized (lock) {
//...
}代替synchronized (users)
无论new了多少个user,他们的id应该是一样的吧?如果这个成立的话,那么可以这样: Java codeHashMap<String, Object> userLockTable = new HashMap<String, Object>();
synchronized (userLockTable) {
if (!userLockTable.containsKey(account)) {
userLockTable.put(account, new Object());
}
}
Object lock = userLockTable.get(account);
…
[/Quote]用了这段代码,测试,结果还是有没同步的问题public int sendMessage(String account, String password, String destmobile,
String msgText) {
try {
HashMap<String, Object> userLockTable = new HashMap<String, Object>();
synchronized (userLockTable) {
if (!userLockTable.containsKey(account)) {
userLockTable.put(account, new Object());
}
}
Object lock = userLockTable.get(account);
synchronized (lock) {
Users users = daoService.getUserByAccount(account);
int userid = users.getUser_id();
发送;
users.setMoney_remain(users.getMoney_remain() - msgCount); //这里是改费用
daoService.modifyUser(users);
}
} catch (Exception ex) {
log.info("|SendMessage error: " + ex);
if (ex instanceof java.lang.IndexOutOfBoundsException)
return -88;
else
return -9;
}
return taskID;
} }
那是因为你是在sendMessage()里new了userLockTable。
这样每次调用sendMessage()当然就生成一个新的hash表了。
HashMap<String, Object> userLockTable = new HashMap<String, Object>();
要放在sendMessage()的外面,至于是不是static的,就取决于你的泪是什么样的。
我放在外面怎么放?
明白我意思么?
分我一会再加,帖子一定结,谢谢大家的帮忙了,特别是xstom19
他们调用这个就可以发信息了,不用管里面的内容
但现在我的这个函数没有作到线程同步,有很大问题
我可以改函数里的东西,但函数的头不能改,也不能把函数包含在别的函数里,那样没效果呀的。
就像这样:public class SomeClass{
private HashMap <String, Object> userLockTable = new HashMap <String, Object>();
public int sendMessage(String account, String password, String destmobile,
String msgText){
//...
}
}
其实我想说的的是数据库加个sequence的字段
取得时候字段一道取出来
比如说是1
然后提交的时候判断下字段是否为1
不是回滚
是1的话提交并且字段自增1
private SMSDaoService daoService = SMSDaoServiceFactory.getDaoService();
private HashMap <String, Object> userLockTable = new HashMap <String, Object>();
public int sendMessage(String account, String password, String destmobile,
String msgText) {
try {
synchronized (userLockTable) {
if (!userLockTable.containsKey(account)) {
userLockTable.put(account, new Object());
}
}
Object lock = userLockTable.get(account);
synchronized (lock) {
Users users = daoService.getUserByAccount(account);
int userid = users.getUser_id();
发送;
users.setMoney_remain(users.getMoney_remain() - msgCount); //这里是改费用
daoService.modifyUser(users);
}
} catch (Exception ex) {
log.info("|SendMessage error: " + ex);
if (ex instanceof java.lang.IndexOutOfBoundsException)
return -88;
else
return -9;
}
return taskID;
} }
}
结果扣费只扣了88
还有12估计是没同步
具体要查日志,应该就是这个同步问题,我查了N次了另外,如果代码没错,那是不是TOMCAT内存没清,新代码没生效的原因
是不是要删掉tomcat 的WORK目录再试下??
public class Main2 {
private HashMap<String, Object> userLockTable = new HashMap<String, Object>(); public int sendMessage(String account) {
synchronized (userLockTable) {
if (!userLockTable.containsKey(account)) {
userLockTable.put(account, new Object());
}
}
Object lock = userLockTable.get(account);
synchronized (lock) {
User user = new User();
int c = user.getCount();
c = c + 1;
user.setCount(c);
System.out.println(Thread.currentThread().toString() + ":" + user.getCount());
}
return 0;
} public static void main(String[] args) {
final Main2 main = new Main2();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable(){ @Override
public void run() {
while(true) {
main.sendMessage("");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } });
thread.start();
}
}}class User{
private static int COUNT = 0; public int getCount() {
return COUNT;
} public void setCount(int count) {
COUNT = count;
}}
如果你刷新了work目录还不行得话,那问题可能就出在你是怎么调用BusinessService的sendMessage方法了。
不过你的BusinessService应该是单例的吧,如果BusinessService也是被new了多个的话,那要把userLockTable 声明成static的。
我那个我把work目录删除后,计算费用还是不对,
我现在看日志,看是不是同步的问题
等晚点我加了分再结帖,毕竟花了那么多时间,不加不厚道。按道理这个小程序和我的程序是一模一样的机制了,应该是不回有问题了呀!
我先查日志吧。
问题到这里基本结束了,从这个问题中我向大家学到了很多新知识,
谢谢大家的热心帮忙,以后我也要多来CSDN答帖子,来提高自己
因为要一天以后才能加分,所以明天结帖.