异步并发保持数据一致性

优化异步网络请求与界面交互
本文介绍了一种利用原子操作(AtomicInteger 和 AtomicBoolean)确保界面在数据加载完成前不可点击的方法,通过计数器和标志位的管理,实现了在所有网络请求执行完毕后界面才允许用户进行触碰操作,有效避免了界面崩掉的问题。

问题背景:项目中不断切换界面的过程中网络异步请求【耗时操作】,但是数据并未加载完成,此时界面中触碰事件时候,直接崩掉【频率低但存在】。

解决思路:监听异步操作完成后才让界面上的按钮可以响应单击【触碰】事件,数据在加载完成前,界面上的按钮触碰事件都应该被屏蔽掉。

实现:用原子操作记录每个异步线程执行开始和执行结束,最终通过标志位来判断所有的网路请求是否执行完,如果执行完、数据加载完毕,界面即可点击触碰操作。

【知识点】:原子操作--》AtomicInteger      AtomicBoolean的运用。


操作图如下:

                        

       所有的网络都是异步请求,将第一个获取数据的地方开始计数,第一个网络请求开始的时候开始计时,然后执行完后就将计数器减一,计时器不断的加一和减一,计数器是原子操作保证数据安全性。那么执行完后统计计数器的数,那么当计数器为0的时候,就是说明请求完毕,数据加载完毕。

       Code实现:在网络请求的地方开始计时【建立网络请求的构造方法也行】

  this.doWorking = doWorking;
            if (this.doWorking!=null) {
                this.doWorking.beginThreadTask();
            }

在网络请求数据完毕后:

  if (this.doWorking!=null) {   //在每次请求访问结束的时候,返回请求响应结果的时候将线程计数器减一
                this.doWorking.endThreadTask();
            }
具体在获取数据代码中调用回掉函数就行:

 @Override
    protected void initData() {
        DoWorking.singleRunOk(isWorking, new DoWorking.doWorkingCallBack() {
            @Override
            public void run(DoWorking doWorking) {
                //初始化	date2=new Date();获取用户做题时间


工具代码如下:

**
 * 作者: on 2015/12/28 11:12
 * 邮箱:jd_education@163.com
 * * @Description: (原子操作:用户界面操作的一致性)
 */
public class DoWorking {
    private AtomicInteger count = new AtomicInteger(0);    //原子操作
    private AtomicBoolean isWorking; //= new AtomicBoolean(false); //设置

    //功能:
    //  1. 顶层try住异常
    //  2. 异常发送服务器日志
    //  3. 一个线程运行时,别的线程直接返回
    public static void singleRunOk(AtomicBoolean isWorking, doWorkingCallBack callback){
        DoWorking dwork=new DoWorking();
        dwork.isWorking=isWorking;

        if (isWorking==null) {
            try {
                callback.run(dwork);//用来执行用户的业务逻辑
            } catch (Exception e) {
                SubmitErrorLogToServer.submitLog(e);
            }
        } else {
            //理解isWorking.compareAndSet(false, true):如果实际值【isWorking】和期望值【false】相等就把实际值设置为更新值【true】,并返回true
            //如果实际值isWorking和期望值不相等,那么就将实际值还是设为更新值,返回false
            //总结:实际值最终为更新值,返回值代表的是逻辑是否走通
            if(!isWorking.compareAndSet(false, true)){
                return;
            }
            dwork.count.incrementAndGet();                               //count+1【count用来计数】

            try {
                callback.run(dwork);//用来执行用户的业务逻辑
            } catch (Exception e) {
                if (0 == dwork.count.decrementAndGet()) {
                    isWorking.set(false);        //如果退出到主程序就设为false
                }
                SubmitErrorLogToServer.submitLog(e);
            }
            if (0 == dwork.count.decrementAndGet()) {
                isWorking.set(false);
            }
        }
    }

    //功能:
    //  1. 顶层try住异常
    //  2. 异常发送服务器日志
    public static void safeRun(safeCallBack callback){
        try {
            callback.run();//用来执行用户的业务逻辑
        } catch (Exception e) {
            SubmitErrorLogToServer.submitLog(e);
        }
    }

    public void beginThreadTask() {
        count.incrementAndGet();
    }

    public void endThreadTask() {
        if (0 == count.decrementAndGet()) {
            if (isWorking!=null) {
                isWorking.set(false);
            }
        }
    }

    public interface doWorkingCallBack {     //回掉函数
        void run(DoWorking dwork);
    }

    public interface safeCallBack {     //initDa函数
        void run();
    }
总结:基本上用接口回掉即可实现效果,其实计数可以理解为记录当前请求线程的id即可,线程池里面还有没有线程。


理解下原子操作:AtomicInteger      AtomicBoolean

AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。















在 Java 高并发环境下,保证数据一致性通常需要结合多种机制和策略,以应对并发读写、缓存与数据库同步、以及分布式环境下的协调问题。以下是一些关键技术和实现方法: ### 三、并发控制机制 在 Java 中,可以通过 `synchronized`、`ReentrantLock`、`ReadWriteLock` 等机制来控制线程间的访问顺序,从而保证共享资源的线程安全性和一致性。例如: ```java import java.util.concurrent.locks.ReentrantLock; public class DataConsistency { private final ReentrantLock lock = new ReentrantLock(); private int sharedData = 0; public void updateData(int value) { lock.lock(); try { sharedData += value; } finally { lock.unlock(); } } } ``` 这种方式适用于单机环境下的并发控制,但不适用于分布式系统[^2]。 ### 四、乐观锁与版本控制 乐观锁适用于读多写少的场景,通过版本号或时间戳来判断数据是否被其他线程修改过。在数据库层面,可以使用 `CAS`(Compare and Set)操作或 `MVCC`(多版本并发控制)技术。例如在 Java 中使用 `AtomicInteger`: ```java import java.util.concurrent.atomic.AtomicInteger; public class OptimisticLockExample { private AtomicInteger version = new AtomicInteger(0); public boolean update(int expectedVersion, int newValue) { return version.compareAndSet(expectedVersion, newValue); } } ``` 乐观锁在高并发场景下性能较好,但在冲突频繁时可能导致大量重试[^2]。 ### 五、缓存与数据库一致性策略 在实际应用中,缓存(如 Redis)与数据库的同步是常见的数据一致性问题。一种常见的做法是: - **写操作**:先更新数据库,然后异步淘汰缓存; - **读操作**:优先从缓存读取,缓存未命中时从数据库加载并写入缓存。 例如: ```java public class CacheDatabaseConsistency { private Cache cache; private Database db; public Object getData(String key) { Object data = cache.get(key); if (data == null) { data = db.get(key); if (data != null) { cache.set(key, data); } } return data; } public void updateData(String key, Object newData) { db.update(key, newData); cache.delete(key); // 异步删除缓存 } } ``` 这种方式虽然可能在短时间内存在缓存与数据库不一致的情况,但整体性能较好,适合大多数业务场景[^3]。 ### 六、分布式一致性方案 在分布式系统中,可采用以下方案: - **2PC(两阶段提交)**:强一致性但性能较差,容易出现单点故障; - **3PC(三阶段提交)**:改进的 2PC,减少阻塞; - **Paxos 或 Raft 协议**:适用于分布式一致性协调,如 Etcd、ZooKeeper 使用 Raft; - **最终一致性方案**:如通过消息队列(Kafka、RocketMQ)进行异步补偿,保证最终一致性。 阿里开源的 Paxos 实现(如 `PaxosStore`)也可用于实现缓存与数据库之间的同步[^3]。 ### 七、事务与数据库隔离级别 在数据库层面,使用事务可以保证操作的原子性、一致性、隔离性和持久性(ACID)。Java 中可通过 JDBC 或 Spring 的事务管理机制实现: ```java @Transactional public void transfer(Account from, Account to, int amount) { from.withdraw(amount); to.deposit(amount); } ``` 设置合适的隔离级别(如 `REPEATABLE READ` 或 `SERIALIZABLE`)有助于防止脏读、不可重复读、幻读等问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值