天天看点

QQ机器人{线程应用篇}

说实话,在写这个应用前,读过《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 = ; 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&pt);
                    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 - ) {
                        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保存缓存数据。

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

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