`
taikeqi
  • 浏览: 4187 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

如何让在一个线程内DAO对象在一个时刻内只被调用一次?

阅读更多
假设现在有线程TA,TA里的run()方法会调用一个DAO借口实现,DAO里会对一个表进行判断,然后写操作。
public class TA extends Thread{
    BusinessDAO  bDAO;

    public TA(BusinessDAO  daoArg){
        bDAO = daoArg;
    }
  
    public run(){
        boolean b = bDAO.findExist(dataList);
        if(!b){
             bDAO.insert(dataList);
        }else{
             bDAO.update(dataList);
        }
    }
}

请问我现在如何做到就是run()方法体内的代码在一个时刻内只有一个实例在运行?也就是说
bDAO.insert(dataList);这行代码在运新的时候,系统内只有一个dataList被传入,然后跟我的数据库系统已有的数据比较,然后得出结论,然后更新数据库状态。
   这样做的原因是我现在的系统老是出现这样的情况,连续传2次dataList,按说第一次判断系统中没有这个datalist后,将这个dataList的值写入到数据库,那么第2次应该会判断出已经有了这个数据,但是实际情况不是,第二次判断仍然得出是没有的结论。而且在传入多次的情况下,结果都是一样。
   这种结果从表面上看来就好像2次上传,第一次和第二次毫无关系一样,难道是2次是并发执行了?

   请问这可能是什么原因造成的?那么我如何控制这种并发,保证我第一次判断,然后写入,然后才执行我第二次判断,第二次写入。
   这个bug跟我的数据库的事务设置有关吗?
   怎么解决这个问题? 还是要将我的程序设置成Singleton模式的? 怎么将我的程序设置成singleton模式的?

我的程序的实际架构是:Struts1.2+Spring2.0+iBatis3,以上的代码我抽象了一下。 线程TA是通过Spring的
org.springframework.core.task.TaskExecutor来调度的。

分享到:
评论
20 楼 no7beckham 2008-04-26  
首先,在线程的run方法里加synchronized没有作用,这个关键字是保证被访问的资源在同一时间只能被一个线程访问,其他的线程必须等到该线程访问完毕,并释放syn的锁资源的时候才能被允许访问。
其次,必须保证find和insert/update方法的一致性,只对find或insert等单独加synchronized是没有用的,想想a线程run的时候首先访问dao的find方法,b线程此时也要访问dao的find方法,因为find方法是同步的,b线程会等待到a线程执行完find方法后才能进入find方法,但是这个时候不能保证a线程按照执行顺序已经把数据insert到数据库了,所以,应该把find和insert放入同一个synchronized,才能保证不会有重复的数据被insert
19 楼 yelaiwei 2008-04-25  
用ThreatLocal 类设置一个全局变量可以解决这个问题把
18 楼 realreal2000 2008-04-25  
这个问题的关键是datalist是怎么拿到的,数据库怎么定义的表还有ibats怎么定义find(datalist)返回true,如果是hibernate的话,如果datalist是自己生成的,那么你怎么find也找不到啊,因为key不一样,自然是新茶如一条。还是实验一下datalist什么情况下返回true在确定是不是同步问题,还是什么问题。
17 楼 realreal2000 2008-04-25  
这个问题的关键是datalist是怎么拿到的,数据库怎么定义的表还有ibats怎么定义find(datalist)返回true,如果是hibernate的话,如果datalist是自己生成的,那么你怎么find也找不到啊,因为key不一样,自然是新茶如一条。还是实验一下datalist什么情况下返回true在确定是不是同步问题,还是什么问题。
16 楼 javazhujf 2008-04-24  
楼上有见地,赞成
15 楼 pf_miles 2008-04-24  
万一是集群,那么sync代码段就没用了,最好还是加入事务,并提升隔离级别,我想数据库很少是分布式的吧?
也可以加一个timeStamp类型字段,然后实现一个乐观锁。
14 楼 wangshare 2008-04-23  
同意楼上的观点。
这是一个并发的问题,解决的办法通过在线程中增加同步操作,可以解决并发中脏读问题,如何不是和数据库打交到,和其他资源(比如文件存储),我觉得这种方法比较合适。在一般Web应用中,数据库的并发问题,首先需要在事务中进行持久化操作,在事务开始以后,读取数据判断当前数据的状态,然后进行操作。同时需要设置事务隔离级别:RepeatableRead和Serializable都可以解决脏读的问题。
13 楼 kakaluyi 2008-04-22  

可能没个人都有自己理解,你们new 两个同样Thread对象start试试看锁的缩不住
楼主本来就是做个实验,一般Web开发中需要把共有对象的资源继承Thread类吗,本来讨论的就不是实际情况。
12 楼 咖啡豆子 2008-04-21  
kakaluyi 写道
javazhujf 写道
taikeqi 写道
可不可以这样做?
将方法
public run(){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}

改成

public run(){
synchronized(this){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}
}



run方法是属于某个线程的方法,不会涉及到多线程的并发。
发生问题的原因是有很多线程也就是很多run方法并发调用DAO或者说并发操作数据库,
所以解决问题的地方也是在DAO或者数据库。
向上面的修改方法我想是不起作用的

楼上的说错了吧?
synchronized(this)就是用来防止多线程共享资源的问题,像上面那样加锁,我觉的可行,不过要加事务提交,不然insert的dataList还是不存在吧,并等事务提交好再让另一个线程执行应该不会错的


每个线程好象都有自己的对象啊,synchronized(this)貌似锁不住吧?
11 楼 taikeqi 2008-04-21  
结合上面的讨论,经过自己尝试,发现无论是: synchronized(this){ },
还是将 DAO封装到一个方法,都不起作用。研究发现:
1)DAO在Spring的配置中缺省都是singleton的。service也是!!!
2)synchronized(this)是对这个TA对象加锁,这个也没用,因为我的TA对象是每次都初始化一个,拥有不同实例,synchronized(this)也没用。
最后,我的解决方法是:


[align=left][align=right][align=center][align=left]
public class TA extends Thread{ 
 private static final String done = "done";

BusinessDAO bDAO; 

public TA(BusinessDAO daoArg){ 
bDAO = daoArg; 
} 

public run(){ 
synchronized(done){
boolean b = bDAO.findExist(dataList); 
if(!b){ 
bDAO.insert(dataList); 
}else{ 
bDAO.update(dataList); 
} 
} 
} 
}
[/align][/align][/align][/align]

10 楼 skyisxbox 2008-04-20  
public void run
9 楼 fch0402 2008-04-20  
明明就是一个事务的问题,用的着这么麻烦吗,还是二楼的方法比较好,我觉得
8 楼 kakaluyi 2008-04-18  
是吗,我只是就是论事
楼主的主题如下:
主题:如何让在一个线程内DAO对象在一个时刻内只被调用一次?
,既然楼主要把dao封装到线程里,那在同一个时刻DAO对象要被只被调用一次当然要同步了,当然要不然new两个线程对象,同时执行的话那同一时刻不是有两个dao对象,进行对公共数据的操作了吗
当然你的想法是对的,一般我们只对调用共享资源的方法同步。
7 楼 javazhujf 2008-04-18  
是我说错了,在run里同步没有问题,
不过按照封装的原则还是最好把同步操作封装到你说的“某个公共资源的操作”的方法里,
也就是楼主的DAO中。
6 楼 kakaluyi 2008-04-18  
是的但是run方法里面涉及的资源操作
run(){
某个公共资源的操作,
比如你的存款余额,
},
既然继承了线程那这么说,这个类在生命周期内就不会只被调用一次,而且可能是不同的人同时调用,假如你的存款是1000,然后当扣了七七八八的费用电话,水电时候(假设扣了1000),你又去取了1000因为没有加锁,你钱也取了,费用也扣了那不是high死你了,那请问这是并发操作吧,我可以让它在两个线程里跑起来
你觉的不需要synchronized(this)起来吗
5 楼 javazhujf 2008-04-18  
run方法是线程实例的方法,它只是在某个线程内执行。
4 楼 kakaluyi 2008-04-18  
javazhujf 写道
taikeqi 写道
可不可以这样做?
将方法
public run(){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}

改成

public run(){
synchronized(this){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}
}



run方法是属于某个线程的方法,不会涉及到多线程的并发。
发生问题的原因是有很多线程也就是很多run方法并发调用DAO或者说并发操作数据库,
所以解决问题的地方也是在DAO或者数据库。
向上面的修改方法我想是不起作用的

楼上的说错了吧?
synchronized(this)就是用来防止多线程共享资源的问题,像上面那样加锁,我觉的可行,不过要加事务提交,不然insert的dataList还是不存在吧,并等事务提交好再让另一个线程执行应该不会错的
3 楼 javazhujf 2008-04-18  
taikeqi 写道
可不可以这样做?
将方法
public run(){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}

改成

public run(){
synchronized(this){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}
}



run方法是属于某个线程的方法,不会涉及到多线程的并发。
发生问题的原因是有很多线程也就是很多run方法并发调用DAO或者说并发操作数据库,
所以解决问题的地方也是在DAO或者数据库。
向上面的修改方法我想是不起作用的
2 楼 taikeqi 2008-04-18  
可不可以这样做?
将方法
public run(){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}

改成

public run(){
synchronized(this){
boolean b = bDAO.findExist(dataList);
if(!b){
bDAO.insert(dataList);
}else{
bDAO.update(dataList);
}
}

1 楼 javazhujf 2008-04-18  
这和单例无关,即使只有一个实例,那么如果不进行同步的话还是有线程并发。
问题的原因是线程并发的时候,第一线程插入DB表后还没有commit时,第二线程读取了这个表的纪录,当然就会出现你说的问题。
所以解决的方法是:
1、DAO方法执行开始就锁表,如果失败则等待直到超时;
2、Spring可以通过配置只生成一个实例,把BusinessDAO做成单例,然后注入,
但是下面的代码需要写到一个方法内并进行同步。
boolean b = bDAO.findExist(dataList); 
if(!b){ 
bDAO.insert(dataList); 
}else{ 
bDAO.update(dataList); 

改成
bDAO.do(dataList)
void do() {
    boolean b = findExist(dataList); 
    if(!b){ 
        insert(dataList); 
    }else{ 
        update(dataList); 
    }
}

相关推荐

    DAO多线程的技巧(31KB)

    DAO多线程的技巧(31KB)

    DAO多线程的技巧.rar_dao

    这通常需要一个中间件(如MyCat、ShardingSphere等)来协调。 - **分布式事务**:在分布式系统中,可能需要处理跨数据库的事务,这时可以采用两阶段提交(2PC)、补偿事务(TCC)或基于Saga的分布式事务解决方案。 ...

    java多线程面试题

    这是因为这些方法必须在一个同步块内调用,并且调用它们的线程必须拥有该对象的锁。 ### 6. Sleep()、Suspend()、Wait()方法的区别 - **Thread.sleep()**:使当前正在执行的线程暂停执行指定时间,释放CPU但不释放...

    使用DAO模式实现宠物数据更新

    1. **实体类(Entity)**:在这里,我们可以创建一个`Pet`类,它代表宠物对象,包含宠物的相关属性如ID、名称、品种等。 ```java public class Pet { private int id; private String name; private String ...

    多线程面试题

    5. **InvalidMonitorStateException**:当一个线程未获得对象的锁时,试图调用wait()、notify()或notifyAll()会抛出此异常。这是因为在非同步上下文中调用这些方法是非法的。 6. **Sleep()、suspend()和wait()的...

    关于java dao的入门介绍

    在DAO中,通常需要根据业务需求来决定是让DAO自身负责事务管理还是将其委托给调用DAO的对象。例如,清单2展示了调用者管理事务的例子,其中事务的开始和结束都在DAO外部完成。 **异常处理**是确保系统稳定性和容错...

    国外JAVA面试多线程问题分享.pdf

    - `Thread.run()`只是一个普通方法,直接调用它不会启动新线程,而是在线程当前上下文中执行,相当于在主线程或其他已存在的线程中运行。 4. **什么是ThreadLocal类?如何使用?** `ThreadLocal`为每个线程提供了...

    演示如何用MFC和DAO(数据访问对象)写一个CGI扩展服务器,程序动

    在本文中,我们将深入探讨如何使用Microsoft Foundation Class (MFC)库和Data Access Objects (DAO)来构建一个CGI(Common Gateway Interface)扩展服务器。CGI服务器是一种在Web服务器上运行的程序,它能接收HTTP...

    32.8、高并发线程1

    `ThreadLocal`的中文名称为“线程局部变量”,它允许每个线程拥有变量的一个独立副本,确保了在多线程环境下变量的独立性,避免了线程之间的数据干扰。 线程安全问题通常出现在共享资源的并发环境中,例如,当多个...

    Spring中DAO被循环调用的时候数据不实时更新的解决方法

    在默认情况下,Spring使用传播行为`PROPAGATION_REQUIRED`,这意味着如果方法在一个事务中被调用,那么它也会在一个事务中执行。在`SecurityService`中的`checkUserInfo`方法中,即使没有显式地添加`@Transactional`...

    使用DAO访问数据库的例子(189KB)

    DAO(Data Access Object)模式是一种常见的软件设计模式,它在应用程序和数据库之间提供了一层抽象,使得业务逻辑层与数据存储层分离。DAO的主要目的是为了简化数据库操作,提高代码的可测试性和可维护性。在这个...

    hibernate通用dao

    - **SessionFactory**: 代表数据库会话工厂,是线程不安全的,通常在应用启动时创建,整个应用生命周期内只存在一个SessionFactory实例。 - **Session**: 数据库会话,处理与数据库的交互,它是线程绑定的,用于执行...

    Dao金山词霸数据库代码

    "Dao金山词霸"是一个专为C++开发者设计的数据库代码库,它提供了与金山词霸数据交互的能力,使得开发者能够轻松地在自己的应用中集成丰富的词汇资源。 Dao金山词霸的核心功能在于它的数据库操作接口,这些接口使得...

    hibernate封装.pdf

    在Java开发中,Hibernate是一个非常流行的ORM(对象关系映射)框架,它允许开发者通过面向对象的方式操作数据库,极大地简化了数据库操作。在提供的代码片段中,我们可以看到一个`HibernateDao`类,它是对Hibernate...

    基于Spring+Ibatis的安全线程实现

    Spring作为一个强大的依赖注入(DI)容器,常用于管理对象的生命周期和协作,而Ibatis则是一个轻量级的SQL映射框架,它简化了数据库操作,将SQL语句与Java代码分离。在这个场景下,我们关注的重点是如何在多线程环境...

    导出成多个sheet 和servlet与dao的使用

    在Excel文件中,Sheet是用于组织数据的单元,一个Excel文件可以包含多个Sheet。在Java中,我们可以使用Apache POI库来操作Excel文件,创建并填充多个Sheet。Apache POI提供了HSSFWorkbook(对应旧的.xls格式)和...

    java面试题

    当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步。在很多情况下采用异步往往更有效率。 数据库三大范式? 答:1:确保每列都是不可分割的原子值 2...

    第5章DAO设计模式[定义].pdf

    DAO模式的引入解决了这个问题,它将数据库操作封装在独立的对象中,使得业务层只需要与这些对象交互,而无需直接操作数据库。 1. 数据库连接类:这是DAO模式的基础,负责建立和关闭数据库连接。通常,这个类会包含...

    j2ee重构DAO&MVC等模式.ppt

    首先,简单工厂模式是一种用于创建对象的模式,它通过一个静态方法来生产对象,减少了客户端代码直接new对象的复杂性。然而,当产品种类增加时,简单工厂模式需要修改,这违背了开闭原则。为了解决这个问题,可以...

    Visual C++编程技巧精选500例.pdf

    222 如何从文本文件中读取一个字符串? 第12章 文件与文件夹属-操作 223 如何判断文件只读属性? 224 如何设置文件只读属性? 225 如何判断文件隐藏属性? 226 如何设置文件隐藏属性? 227 如何判断文件归档属性? 228 ...

Global site tag (gtag.js) - Google Analytics