欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程简单应用场景

程序员文章站 2022-03-05 16:53:12
...

多线程简单应用场景

注:最近想研究多线程,网上搜索资料,发现多线程的资料很多,但是应用场景却很少,大部分都是指出了怎么开启多线程,却没有一个多线程结合实际场景的举例.比如说最经典的多线程就是多窗口卖票Model,但是实际工作中我们应该有service层参与啊,我们要处理业务才对.基于以上原因,本文简单提出一个多线程的应用场景,并进行测试,当然,之前并没有多线程实际的开发经验,只是这个demo经过测试之后可以用,在当前的场景下能够有效的提高效率,所以贴出来和大家分享,当然Demo中也有很多问题没有解决,希望大家(尤其是写过多线程业务的大腿)能够指出其中的不足,最好能举一个更加具体的多线程使用场景.感激不尽.
let’s go!

  • 业务场景概述
  • 我们在电商业务冲,有一个非常重要的点,就是钱相关的业务,钱相关的业务最重要的就是订单,订单中最重要的环节就是支付,有点跑题了,这样讲,我们只要做的是互联网业务,需要盈利,我们一般来说就会涉及到支付,而现在最普遍的应该就是支付宝支付或者微信支付,我们以支付宝支付为例(如果公司有自己的支付渠道,另算,笑),我们支付成功的时候,订单信息就会走第三方支付接口,支付结束之后,我们自己的项目中会有一份订单的详情,而第三方支付接口,以支付宝为例,支付宝的系统中也会有一份我们订单的详情(主要包括支付状态,订单金额,是否成功等等).
    好,上面铺垫的差不多了,重点来了,我们今天的业务场景就是订单的校对,我们项目(数据库)自己的订单和支付宝的订单进行校对,支付宝保证了订单状态的100%正确率,而我们不能保证自己的项目100%正确,所以就需要校对,再粗暴一点的讲,以一个月为周期,查询支付宝本月账单,总金额为1000000,而我们项目里这个月订单总金额为1000001,差了1块钱,如果不校对订单的话,怎么办,这个月差1块,下个月可能就差10000,所以说只要有支付的业务,校对订单是必不可少的.
    前提说的差不多了,下面就来说说为什么要引入多线程.
    我们都知道多线程是为了处理大量数据(或者大量请求,意思差不多)的.现在这样讲,我们项目每天大概产生100w订单,我们需要每天校对订单,不然的话,每个月校对数据量就太大了,现在我们思考一下,我们应该怎样来进行订单的校对,最简单的讲,可能都不需要思考,写一个100w的for循环,然后差一条比对一条,这样我想服务器着火了也不一定能跑完.还有就是万一校对的数据比较多,万一花费的时间超过一天怎么办,这样我们每天都校对不完,更不用说有错误的订单需要去处理了.
    这时候我们就考虑到多线程,(补充一点,这里暂时没有使用到大数据的相关技术,只是使用多线程来实现).我们可以启动多个线程来处理校对的工作,这样的话,效率就是几何倍数的提高.
    好了,前提说的差不多了,把我漏洞百出的代码弄出来,给大家鉴赏一下,提提建议.

import java.util.List;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.itcast.domain.User;
import cn.itcast.domain.Userdemo;
import cn.itcast.service.UserService;
import cn.itcast.service.UserdemoService;



public class ThreadUserTest implements Runnable {

    // private int ticketCount = 100;// 总的票数,这个是共享资源,多个线程都会访问
    Object mutex = new Object();// 锁,自己定义的,或者使用实例的锁
    UserService userService = SpringContextUtil.getBean("userServiceImpl");
    UserdemoService userdemoService = SpringContextUtil.getBean("userdemoServiceImpl");

    List<User> userList1 = userService.selectUserBySize(1);
    List<Userdemo> userList2 = userdemoService.selectUserBySize(1);

    private int ticketCount = userList1.size();// 总的票数,这个是共享资源,多个线程都会访问

    public void sellTicket() {
        synchronized (mutex)// 当操作的是共享数据时,
                            // 用同步代码块进行包围起来,执行里面的代码需要mutex的锁,但是mutex只有一个锁。这样在执行时,只能有一个线程执行同步代码块里面的内容
        {
            if (ticketCount > 0) {
                ticketCount--;
            } else {
                System.out.println("执行完成");
                return;

            }
        }

    }

    public void run() {
        while (ticketCount > 0)// 循环是指线程不停的去卖票
        {
            sellTicket();
            /**
             * 在同步代码块里面睡觉,和不睡效果是一样
             * 的,作用只是自已不执行,也不让线程执行。sleep不释放锁,抱着锁睡觉。其他线程拿不到锁,也不能执行同步代码。wait()
             * 可以释放锁 所以把睡觉放到同步代码块的外面,这样卖完一张票就睡一会,让其他线程再卖,这样所有的线程都可以卖票
             */
            try {
                // run方法注入service失败,service为null,目前理解为service为共有资源,不能线程共享,希望大腿们提出新的见解
                /*
                 * User user1 = userService.selectUserById(ticketCount);
                 * Userdemo user2 = userdemoService.selectUserById(ticketCount);
                 */
                /*
                 * 当前这个场景代码实现不足的地方,第一,我现在的数据是能保证一一对应的,也就是说主键id是对应的
                 * 实际业务中,我们应该是有一边是遍历取出来,另一个需要根据id查出来,,查的这个业务就比较复杂了,是根据id直接查数据库(
                 * 这个方法怎么实现不知道,我的service线程里注入不进来 另一个就是在list里面查了:
                 * 这里涉及到的问题就是遍历list,查出id,然后list中再去除已经验证的id对应的user对象
                 * 有点绕,需要精心捋一捋逻辑
                 * 
                 */
                User user1 = userList1.get(ticketCount);
                Userdemo user2 = userList2.get(ticketCount);
                if (!user1.getUserId().equals(user2.getUserId())) {
                    System.out.println(
                            "-------------------两张表数据不相同,用户id:" + user1.getId() + "---demo表id:" + user2.getId());
                    System.out.println("-------------------对比结果不同的线程名:" + Thread.currentThread().getName());
                } else {
                    System.out.println("两张表数据相同" + Thread.currentThread().getName() + "----" + ticketCount);
                }
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//补充:当前的循环中,应该根据id查询另一个list中的对应id的对象中的数据.后来思考这个问题,可以把list换成map,id为键,对象为value,这样作为被查询的list<user>,应该能提高不小效率

这个是线程类的代码,是修改卖票demo来的,有一些参数名并没有改动.

    ThreadUserTest usert = new ThreadUserTest();
            Thread th1 = new Thread(usert, "窗口1");// 每个线程等其他线程释放该锁后,才能执行
            Thread th2 = new Thread(usert, "窗口2");
            Thread th3 = new Thread(usert, "窗口3");
            Thread th4 = new Thread(usert, "窗口4");

            th1.start();
            th2.start();
            th3.start();
            th4.start();

启动线程就是上面这段代码,没什么特殊的,先说一下我的理解吧,有错误的地方欢迎指出:
start()调用的是run()方法,我们可以看到ruan里面就是一个循环,或者说就是一个for循环,启动几个线程就相当于启动了几个for循环,循环的数据是共享的.这样的话就能保证我的多线程启动之后,共同处理一块数据,也就是我们业务场景里面的那些订单.
- 问题
- 上面的代码漏洞比较多,大家都能看出来,我先提几个目前比较困惑的地方,大家一起来讨论讨论:
- 1.我上面写的这个demo,线程启动是写在了controller中,也就是需要项目启动,才能正常的创建线程.如果放在test里面,拿不到service,测试的时候会报空指针.在这里我的理解是项目没启动的话,spring容器没有把servicebean对象创建,所以拿不到,但是为什么其他的随便写个@autowired,service,调用service方法是没有问题的,难道只是线程里面 拿不到吗
- 2.有个困扰了好久的问题,查的资料比较少,就是线程实体run()和excute()两个方法里面为什么不能引入service.难道是service属于共享资源,不能被线程独享么,这一块到目前为止也不是很懂
多线程这边的问题比较多,尤其是共享资源的处理,现在还在研究中,不是特别明白,上面的demo只是为了实现这个业务场景而强行建立而来.如果有什么严重的逻辑问题,欢迎指出.
- * 多线程这边的问题比较多,尤其是共享资源的处理,现在还在研究中,不是特别明白,上面的demo只是为了实现这个业务场景而强行建立而来.如果有什么严重的逻辑问题,欢迎指出.因为小公司用不到多线程,也就没有练习的场景,大公司招人就需要会多线程的,这样只能逼着自学啊,,,,能力有限,写的不好的地方欢迎大家指正*

相关标签: 多线程 应用