天天看点

多线程简单应用场景多线程简单应用场景

多线程简单应用场景

注:最近想研究多线程,网上搜索资料,发现多线程的资料很多,但是应用场景却很少,大部分都是指出了怎么开启多线程,却没有一个多线程结合实际场景的举例.比如说最经典的多线程就是多窗口卖票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();
    List<Userdemo> userList2 = userdemoService.selectUserBySize();

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

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

            }
        }

    }

    public void run() {
        while (ticketCount > )// 循环是指线程不停的去卖票
        {
            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();
            } 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只是为了实现这个业务场景而强行建立而来.如果有什么严重的逻辑问题,欢迎指出.因为小公司用不到多线程,也就没有练习的场景,大公司招人就需要会多线程的,这样只能逼着自学啊,,,,能力有限,写的不好的地方欢迎大家指正*

继续阅读