QQ机器人{线程应用篇}

来源:互联网 发布:mysqlsla windows 编辑:程序博客网 时间:2024/05/01 07:43

说实话,在写这个应用前,读过《Java并发编程的艺术》,但对于并发以及多线程的应用不是很多,现在写完后也算有了一定的了解,所以这篇文章就来聊聊项目中用到的一些线程框架。

如果对于流程不熟悉的还是先看下前面几篇文章吧,这里简单提一下线程应用的地方。

贴下代码吧,这样更直观。

package org.cool.qqrobot.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import org.cool.qqrobot.common.Const;/** * 线程池(单例,将各种常用线程池封装在此类,方便统一调用) * @author zhoukl * */public final class ThreadPool {    private static ThreadPool threadPool = new ThreadPool();    private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(Const.FIXED_THREAD_POOL_NUM);    private static ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(Const.SCHEDULED_THREAD_POOL_NUM);    private ThreadPool() {}    public static ThreadPool getInstance() {        return threadPool;    }    public ExecutorService getFixedThreadPool() {        return fixedThreadPool;     }    public ScheduledExecutorService getScheduledThreadPool() {        return scheduledThreadPool;    }}

以上代码是定义的线程池,下面贴下具体应用。

第一处:二维码验证
服务端http请求获取到二维码后立即将二维码返回给了用户,异步线程开始轮询二维码的状态。

private void loginCheck(ProcessData processData) {        logger.info("|{}|--二维码验证中", ProcessVar.getProcessId());        ThreadPool.getInstance().getFixedThreadPool().execute(new Runnable() {            @Override            public void run() {                for (int i = 0; i < Const.CYCLE_NUM; i++) {                    MyHttpRequest checkRequest = new MyHttpRequest();                    checkRequest.setUrl("https://ssl.ptlogin2.qq.com/ptqrlogin?webqq_type=10&remember_uin=1&login2qq=1&aid=501004106&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-0-9604&mibao_css=m_webqq&t=undefined&g=1&js_type=0&js_ver=10181&login_sig=&pt_randsalt=0");                    MyHttpResponse checkResponse = new MyHttpResponse();                    try {                        checkResponse = processData.getMyHttpClient().execute(checkRequest);                    } catch (Exception e) {                        processData.setGetCode(false);                        logger.error("二维码登录状态获取异常", e);                    }                    boolean firstLoginSuccess = false;                    if (MyHttpResponse.S_OK == checkResponse.getStatus()) {                        firstLoginSuccess = firstLogin(processData, checkResponse);                    } else {                        processData.setGetCode(false);                    }                    // 每次轮询休息一段时间,不然太频繁                    threadSleep();                    if (firstLoginSuccess) {                        break;                    }                    // 轮询时间结束,用户还未扫描登录,自动标识为登录失败(不然下次不能获取二维码,刷新页面二维码不变)                    if (i == Const.CYCLE_NUM - 1) {                        processData.setGetCode(false);                    }                }            }        });    }

这里的轮询采用for循环控制,因为不需要永久轮询。部分方法未给出实现部分,但也没删是因为有涉及到过程数据(processData)的存取,之前只是提及,没有代码演示,现在贴出来可能会直观些。

第二处:用户登录信息记录
这部分没有实时同步需要,因此采用异步记录日志,线程池应用和上面一致。

// 异步记录登录用户信息// 登录成功后,记录当前登录日志的id,当用户主动退出时,再次更新登出时间ThreadPool.getInstance().getFixedThreadPool().execute(new Runnable() {    @Override    public void run() {        logger.info("记录用户登录信息");        robotDao.addLoginInfo(userInfo);    }});

IO读写担心耗时就采用异步线程。

第三处:消息轮询
用户的消息收发需要一直提供服务,所以线程不间断的轮询消息。

private void pollMessageThread(ProcessData processData) {        logger.info("开始轮询消息");        // 把每一个轮询线程对象保存到map中,便于终止轮询(key:qq号,value:Future对象),同时清除登录成功的缓存信息,更新退出时间        Future<?> future = ThreadPool.getInstance().getScheduledThreadPool().scheduleAtFixedRate(new Runnable() {            @Override            public void run() {                // 消息收发...            }        }, Const.INIT_DELAY, Const.PERIOD, TimeUnit.MICROSECONDS);        // 保存当前线程future        CacheMap.threadFuturMap.put(processData.getSelfUiu(), future);    }

这里采用定时任务,循环执行服务的调用。此处还用到了map,因为是多线程并发操作,那么HashMap和HashTable显然都不合适了(至于为什么,其它文章有详细说明,这里就不重复了),所以采用ConcurrentHashMap保存缓存数据。

本来想聊聊多线程的原理什么的,看了下相关的书籍和博客,感觉自己的领悟程度还不够深,所以不敢妄加总结,大牛实在太多了,我等小辈还是多做多学多看吧。

好了,这个年也过的差不多了,又大一岁,好好努力,别辜负了时光~

1 0
原创粉丝点击