在线人员列表逻辑混乱反例
一:背景介绍
本篇博客是对在线人员列表逻辑混乱反例进行的总结和进行的改进。
目的是将经历转变为自己的经验。通过博客的方式分享给大家,大家一起共同进步和提高。
在线人员列表逻辑混乱反例中涉及到的问题
- 类中写了公共变量最后导致数据混乱现象
- 保存数据没有考虑业务的隔夜覆盖导致的逻辑漏洞
涉及到继承,对于this,如果父类有同样的成员最终使用哪一个? - 参数不一致导致后续维护混乱
- mysql由于关联字段类型不一致导致产生索引失效问题,进而产生慢sql
- sql不考虑业务导致有明显的逻辑漏洞
上面的问题主要分为四类问题: - 公共变量导致数据混乱
- 对于在线人员的业务存在逻辑问题(对应上面的 2 4 6 )
- 面向对象继承的问题,this.属性和方法的问题
- mysql由于关联字段类型不一致导致索引失效的问题。
二:思路&方案
类中写了公共变量最后导致数据混乱现象的解决方案
由于线程共享全局变量,并且在实际业务的过程中对全局变量进行了操作(修改),所以在高并发、多线程的条件下会产生公共变量导致数据混乱的现象。
解决方案1 使用ThreadLocal
ThreadLocal叫做线程变量,该变量对于其他线程而言是隔离的,该变量是当前该线程独有的变量。
它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。正是由于每个线程都有自己的实例副本,且与其他线程不可访问,那就不存在多线程共享该变量的问题。
ThreadLocal 常见使用场景
1、每个线程需要有自己单独的实例
2、实例需要在多个方法中共享,但不希望被多线程共享
ThreadLocal的简单使用
多线程环境下未使用ThreadLocal出现数据混乱的问题
public class Client { public static void main(String[] args) { // 定义线程实现接口 Runnable runnable = new Runnable(){ Counter counter = new Counter(); @Override public void run() { counter.count(); } }; // 启动10个线程 for( int i= 0;i< 10;i++) { new Thread(runnable).start(); } } }
public class Counter{ private int number; public void count(){ for (int i = 0; i <=10 ; i++) { number=number+i; } System.out.println(Thread.currentThread().getName()+ "--"+number; } }
实现结果
出现数据混乱的问题,正确计算结果应该都为55
多线程环境下使用ThreadLocal解决数据混乱的问题
public class Client { public static void main(String[] args) { // 定义线程实现接口 Runnable runnable = new Runnable(){ Counter counter = new Counter(); @Override public void run() { counter.count(); } }; // 启动10个线程 for( int i= 0;i< 10;i++) { new Thread(runnable).start(); } } }
public class Counter { private static ThreadLocal<Integer> number = new ThreadLocal<Integer>() { // 重写这个方法,可以修改“线程变量”的初始值,默认是null @Override protected Integer initialValue() { return 0; } }; //计数方法 public void count() { for( int i= 0;i<= 10;i++) { number.set(number.get()+i); } System.out.println(Thread.currentThread().getName()+ "--"+number.get()); } }
实现结果
解决方案2 将全局变量修改为成员变量
public void count(){ int number = 0; for (int i = 0; i <=10 ; i++) { number=number+i; } System.out.println(Thread.currentThread().getName()+ "--"+number); } }
实现效果
对于在线人员的业务存在逻辑问题(对应上面的 2 4 5 7 )的方案
解决思路:由于这里查询的入参带了createDate,如果该学生从第一天到第二天一直没有下课,那么你这儿查询的话由于带了createDate就查询不到该学生的在线数据,在后续的流程中就会重复插入,该学生就会有两条数据,查询的时候就会有数据问题。
解决思路: 对于isOnline参数,上面paramUser中有该参数。不必再进行赋值。这样的好处是参数值保持了一致,降低了复杂度。
**解决思路:**这里主要是对在线人员进行下线处理,1.如果在这里更新了update_time那么就相当于这个课程班级中的所有学生的最近一次登陆时间都是一致的,并且最近一次登陆时间与实际不符。2.如果where条件上加上了create_date作为条件那么如果过了十二点结束课程,那么所有在线的人就无法进行下线处理了。
正确的SQL语句:
面向对象继承的问题
解决思路: 1.子类可以继承父类的所有非private修饰的属性和方法 2.this.方法的问题
面向对象子类与父类中this的使用
实例结构
动物类
public class Animal { protected String name="动物" ; protected String age="18"; public void printName(){ System.out.println(name); } public void printAge(){ System.out.println(age); } }
猫类
public class Cat extends Animal { private String name="猫"; public void printName(){ System.out.println(this.name); System.out.println(super.name); } public void println(){ this.printName(); super.printName(); } }
调用类
public class Client { public static void main(String[] args) { Cat cat =new Cat(); cat.printName(); cat.printAge(); cat.println(); } }
实现结果
结论:
java中继承关系的父子类,相同的方法会被重写
当子类父类中的成员没有重名时,子类都可以通过this去调用。
当成员方法 重名,子类就会将父类中的方法进行重写。如果还想调用父类中的方法只能通过 super去进行调用。
当时成员变量不存在覆盖重写:在子类中只能通过super调用父类的
mysql由于关联字段类型不一致导致索引失效的问题的解决方案
在实际的项目开发过程中,需要特别注意关联字段的数据类型是否一致。吐过不一致将导致索引失效,出现mansql的情况。
索引失效的问题复现
EXPLAIN SELECT aui.ding_phone, aui.ding_name, aui.chaoxing_name, aui.chaoxing_phone FROM (SELECT info_id FROM `arpro_user_course_info` WHERE course_id = '223667994' AND class_id = '55801765' AND is_delete = 0 GROUP BY id)as auci left JOIN arpro_user_info as aui on auci.info_id=aui.id
arpro_user_course_info与arpro_user_info通过info_id与aui.id进行主外键的关联。
arpro_user_info的id类型为varchar类型
arpro_user_course_info的info_id类型为bigint类型
通过EXPLAIN进行sql语句的性能分析
发现arpro_user_info的为全部查询,索引失效了。
将关联字段的类型修改为一致再次进行测试
测试结果
索引生效
四:总结
1.在项目开发过程中,对于公共变量的使用一方面需要慎重,需要考虑是否有并发,多线程的情况,然后根据实际情况选择对应的处理措施。
2.关于项目开发过程中出现的逻辑问题,一方面进行开发一定需要绘制流程图,根据流程图进行代码编写,另一方面及时总结将经历转变为经验。
3.对于面向对象中子类父类的的特点,一方面进行理论学习,一方面特别需要进行实践验证。
4.关于sql语句由于级联关联字段类型不一致,出现索引失效的问题需要特别重视,极易出现性能问题。也需要了解其他情况下索引失效。
五:升华
- 闭环反馈,是成长型思维的核心
- 不怕不知道就怕不知道,扩大认知圈