Netty游戏服务器实战开发(12):线程任务组件开发

导读: 线程在java里面很多,本文不会介绍线程使用,只会介绍大型项目线程组件的开发模式。

一个大型项目中少不了对多线程的使用,在使用多线程的时候,可以使用Java 的API提供的Thread或者Runnable。随着API的丰富和发展,对程序员来说使用和管理线程也变得越来越方便了。例如我们利用线程池来管理线程,很常见的做法就是:

   public void testThread() {
        ExecutorService executorService= Executors.newSingleThreadExecutor();
        executorService.execute(
                ()-> System.out.println("线程任务"));
    }

但是在实战项目中,我们需要对项目中的线程进行管理、和控制是具有严格,通常我们将线程的产生,都集中到一个地方,通过严格的参数控制,对外提供线程对象。工厂模式就很适合这种情况,在jdk1.5以上,提供了threadfactory 这个API,所以项目中我们可能会有这样的写法:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.twjitm.threads.thread;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettyThreadNameFactory implements ThreadFactory {
    Logger logger;
    private String threadName;
    private ThreadGroup threadGroup;
    private AtomicInteger threadNumber;
    private boolean daemon;

    public NettyThreadNameFactory(String threadName) {
        this(threadName, false);
        this.threadName = threadName;
    }

    public NettyThreadNameFactory(String threadName, boolean daemon) {
        this.logger = LoggerFactory.getLogger(NettyThreadNameFactory.class);
        this.threadNumber = new AtomicInteger(0);
        this.threadName = threadName;
        this.daemon = daemon;
        SecurityManager s = System.getSecurityManager();
        this.threadGroup = s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        this.threadName = threadName + "-thread-";
        this.daemon = daemon;
    }

    public Thread newThread(Runnable r) {
        Thread thread = new Thread(this.threadGroup, r, this.threadName + this.threadNumber.getAndIncrement(), 0L);
        if (this.daemon) {
            thread.setDaemon(this.daemon);
        } else {
            if (thread.isDaemon()) {
                thread.setDaemon(false);
            }

            if (thread.getPriority() != 5) {
                thread.setPriority(5);
            }
        }

        if (this.logger.isDebugEnabled()) {
            this.logger.debug("创建线程:" + this.threadName);
        }

        return thread;
    }

    public String getThreadName() {
        return this.threadName;
    }
}

可以控制线程的数量,线程名称,线程是否为守护线程。对外使用时如在启动netty服务器的时候,可以通过如下方式进行设置。
在这里插入图片描述

统一线程任务:

在大型项目系统中,为了统一代码风格,会将线程执行的任务封装到一个任务队列中去执行,,任务队列有很多方式,比如有序任务队列,无序任务队列等。这里我们主要介绍,通过扩展ThreadPoolExecutor 来实现有序队列和无序队列。

首先我们需要定义任务队列实体。

package com.twjitm.threads.thread.task;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author twjitm - [Created on 2018-08-23 17:24]
 * @company https://github.com/twjitm/
 * @jdk java version "1.8.0_77"
 */
public class NettyTaskQueue<V> {

    private boolean comple = true;
    /**
     * 任务队列
     */
    BlockingQueue<V> tasksQueue = new LinkedBlockingQueue<V>();

    /**
     * 下一执行命令
     *
     * @return
     */
    public V poll() {
        return tasksQueue.poll();
    }

    /**
     * 增加执行指令
     *
     * @param
     * @return
     */
    public boolean add(V value) {
        return tasksQueue.add(value);

    }

    /**
     * 清理
     */
    public void clear() {
        tasksQueue.clear();
    }

    /**
     * 获取指令数量
     *
     * @return
     */
    public int size() {
        return tasksQueue.size();
    }

    public boolean isComple() {
        return comple;
    }

    public void setComple(boolean comple) {
        this.comple = comple;
    }

}

定义一个任务实体队列。NettyOrderTaskQueue

package com.twjitm.threads.thread.task;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author twjitm - [Created on 2018-08-23 17:36]
 * @company https://github.com/twjitm/
 * @jdk java version "1.8.0_77"
 * 有序队列
 */
public class NettyOrderTaskQueue<K, V> {

    private final ConcurrentHashMap<K, NettyTaskQueue<V>> taskOrderQueue = new ConcurrentHashMap<K, NettyTaskQueue<V>>();

    /**
     * 获得任务队列
     *
     * @param key
     * @return
     */
    public NettyTaskQueue<V> getTasksQueue(K key) {
        NettyTaskQueue<V> queue = taskOrderQueue.get(key);

        if (queue == null) {
            NettyTaskQueue<V> newQueue = new NettyTaskQueue<V>();
            queue = taskOrderQueue.putIfAbsent(key, newQueue);
            if (queue == null) {
                queue = newQueue;
            }
        }

        return queue;
    }

    /**
     * 获得全部任务队列
     *
     * @return
     */
    public ConcurrentHashMap<K, NettyTaskQueue<V>> getTasksQueues() {
        return taskOrderQueue;
    }

    /**
     * 移除任务队列
     *
     * @return
     */
    public void removeTasksQueue(K key) {
        taskOrderQueue.remove(key);
    }

}

通过ConcurrentHashMap 的独有特性,将任务保存到map中。

为了使用我们定义的任务队列,上面也说到,通过扩展ThreadPoolExecutor 来实现任务调度。因此,编写NettyOrderThreadPoolExecutor类来处理这样的逻辑。

package com.twjitm.threads.common.executor;

import com.twjitm.threads.common.logs.LoggerFactory;
import com.twjitm.threads.entity.AbstractNettyTask;
import com.twjitm.threads.thread.NettyThreadNameFactory;
import com.twjitm.threads.thread.task.NettyOrderTaskQueue;
import com.twjitm.threads.thread.task.NettyTaskQueue;
import org.slf4j.Logger;

import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * 扩展ThreadPoolExecutor 实现有序队列执行
 *
 * @author twjitm - [Created on 2018-08-23 17:45]
 * @company https://github.com/twjitm/
 * @jdk java version "1.8.0_77"
 * <p>
 * https://blog.csdn.net/u013256816/article/details/50403962
 *
 */
public class NettyOrderThreadPoolExecutor extends ThreadPoolExecutor {
    private Logger logger = LoggerFactory.logger;
    private ReentrantLock lock = new ReentrantLock();

    private int maxTasExecutorSize;

    private NettyThreadNameFactory threadNameFactory;

    public NettyOrderThreadPoolExecutor(String threadName, int corePoolSize, int maxTasExecutorSize) {

        super(corePoolSize, maxTasExecutorSize * 2, 30,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NettyThreadNameFactory(threadName));

        this.maxTasExecutorSize = maxTasExecutorSize;
        this.threadNameFactory = (NettyThreadNameFactory) getThreadFactory();

    }

    public NettyOrderThreadPoolExecutor(String threadName, int corePoolSize, int maxPollSize, int maxTasExecutorSize, RejectedExecutionHandler rejectedExecutionHandler) {

        super(corePoolSize, maxPollSize, 30,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
                new NettyThreadNameFactory(threadName), rejectedExecutionHandler);

        this.maxTasExecutorSize = maxTasExecutorSize;
        this.threadNameFactory = (NettyThreadNameFactory) getThreadFactory();


    }

    public NettyOrderThreadPoolExecutor(String threadName, int corePoolSize, int maxTasExecutorSize, RejectedExecutionHandler rejectedExecutionHandler) {

        super(corePoolSize, maxTasExecutorSize * 2, 30,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
                rejectedExecutionHandler);

        this.maxTasExecutorSize = maxTasExecutorSize;
        this.threadNameFactory = (NettyThreadNameFactory) getThreadFactory();
    }


    public NettyOrderThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.threadNameFactory = (NettyThreadNameFactory) getThreadFactory();
        this.maxTasExecutorSize = maximumPoolSize;
    }

    NettyOrderTaskQueue<Long, AbstractNettyTask> poll = new NettyOrderTaskQueue<Long, AbstractNettyTask>();


    /**
     * 添加一个任务到队列里面
     *
     * @param taskId
     * @param task
     * @return
     */
    public boolean addTask(long taskId, AbstractNettyTask task) {
        boolean run = false;
        boolean result = false;
        NettyTaskQueue<AbstractNettyTask> queue = poll.getTasksQueue(taskId);
        lock.lock();
        if (maxTasExecutorSize > 0) {
            if (queue.size() > maxTasExecutorSize) {
                if (logger.isWarnEnabled()) {
                    logger.warn("队列" + threadNameFactory.getThreadName() + "(" + taskId + ")" + "超过最大队列大小设置!");
                }
            }
            result = queue.add(task);
            if (result) {
                task.setTaskBlockingQueue(queue);
                if (queue.isComple()) {
                    queue.setComple(false);
                    run = true;
                }
            } else {
                logger.info(" ADD TASK ERROR");
            }
            if (run) {
                execute(task);
            }
        }
        lock.unlock();
        return result;
    }


    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);

        AbstractNettyTask work = (AbstractNettyTask) r;
        NettyTaskQueue<AbstractNettyTask> queue = work.getTaskBlockingQueue();
        if (queue != null) {
            AbstractNettyTask afterWork = null;
            synchronized (queue) {
                afterWork = queue.poll();
                if (afterWork == null) {
                    queue.setComple(true);
                }
            }
            if (afterWork != null) {
                execute(afterWork);
            }
        } else {
            logger.error("执行队列为空");
        }
    }
}

在上诉代码中,我们看到一个新的类AbstractTask,这是一个抽象的任务,需要自己去实现任务中所具体的业务逻辑。

package com.twjitm.threads.entity;

import com.twjitm.threads.thread.task.NettyTaskQueue;

/**
 * @author twjitm - [Created on 2018-08-23 17:33]
 * @company https://github.com/twjitm/
 * @jdk java version "1.8.0_77"
 * 抽象任务实体
 */
public abstract class AbstractNettyTask implements Runnable {
    private NettyTaskQueue<AbstractNettyTask> taskBlockingQueue;

    public NettyTaskQueue<AbstractNettyTask> getTaskBlockingQueue() {
        return taskBlockingQueue;
    }

    public void setTaskBlockingQueue(NettyTaskQueue<AbstractNettyTask> taskBlockingQueue) {
        this.taskBlockingQueue = taskBlockingQueue;
    }
}

可能细心的同学看到,代码中还有一个重要的变量, private ReentrantLock lock = new ReentrantLock();;对,就是这个,关于他的使用方法和相关知识,不太清楚的同学可以去这看看reentrantlock .
在往队列中添加任务的时候我们需要采用同步锁机制。保证每个任务都是有序执行。

有了同步执行队列的基础,在实现无序队列其实相对也就简单很多。

package com.twjitm.threads.common.executor;

import com.twjitm.threads.entity.AbstractNettyTask;
import com.twjitm.threads.thread.NettyThreadNameFactory;

import java.util.concurrent.*;

/**
 * @author twjitm - [Created on 2018-08-24 15:43]
 * @company https://github.com/twjitm/
 * @jdk java version "1.8.0_77"
 * 无序队列执行器
 */
public class NettyUnorderThreadPollExecutor extends ThreadPoolExecutor {
    public NettyUnorderThreadPollExecutor(int corePoolSize) {
        super(corePoolSize, corePoolSize * 2, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>());
    }

    public NettyUnorderThreadPollExecutor(String name, int corePoolSize) {
        super(corePoolSize, corePoolSize * 2, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new NettyThreadNameFactory(name));
    }

    public NettyUnorderThreadPollExecutor(String name, int corePoolSize, int maxPoolSize) {
        super(corePoolSize, maxPoolSize, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new NettyThreadNameFactory(name));
    }

    public NettyUnorderThreadPollExecutor(String name, int corePoolSize, int maxSize, RejectedExecutionHandler rejectedExecutionHandler) {
        super(corePoolSize, maxSize, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new NettyThreadNameFactory(name), rejectedExecutionHandler);
    }


    public NettyUnorderThreadPollExecutor(String name, int corePoolSize, int maxSize, BlockingQueue blockingQueue, RejectedExecutionHandler rejectedExecutionHandler) {
        super(corePoolSize, maxSize, 30, TimeUnit.SECONDS,
                blockingQueue, new NettyThreadNameFactory(name), rejectedExecutionHandler);
    }


    public void executeTask(AbstractNettyTask task) {
        super.execute(task);
    }
}

在上面的代码中,构造函数中采用的线程池异常处理方式利用默认的方式,也就是RejectedExecutionHandler 。当然,我们可以完全重写它来实现我们自定义拒绝策略。

例如在项目中肯能会看到这样的代码。
在这里插入图片描述

每一种处理方式有各自的区别。

  • AbortPolicy /*丢弃/
  • BlockingPolicy /阻塞/
  • CallerRunsPolicy /直接运行/
  • DiscardOldestPolicy /抛弃老的/
  • DiscardPolicy /删除/

如博主这样编写的一个直接丢弃的方式。

package com.twjitm.threads.thread.policy;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by IntelliJ IDEA.
 * User: 文江 Date: 2018/8/19  Time: 10:52
 * https://blog.csdn.net/baidu_23086307
 * 队列满抛出异常
 */
public class AbortPolicy extends ThreadPoolExecutor.AbortPolicy {
    private String threadName;
    private Logger logger = LoggerFactory.getLogger(AbortPolicy.class);

    public AbortPolicy() {
        this(null);
    }

    public AbortPolicy(String threadName) {
        this.threadName = threadName;
    }

    @Override
    public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
        if (threadName != null) {
            logger.error("THREAD POOL " + threadName + " IS EXHAUSTED, EXECUTOR=" + executor.toString());
        }
        String msg = String.format("Server["
                        + " THREAD NAME: %s, POOL SIZE: %d (ACTIVE: %d, CORE: %d, MAX: %d, LARGEST: %d), TASK: %d (COMPLETED: %d),"
                        + " EXECUTOR STATUS:(IS SHUTDOWN:%s, IS TERMINATED:%s, IS TERMINATING:%s)]",
                threadName, executor.getPoolSize(), executor.getActiveCount(), executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getLargestPoolSize(),
                executor.getTaskCount(), executor.getCompletedTaskCount(), executor.isShutdown(), executor.isTerminated(), executor.isTerminating());
        logger.info(msg);
        super.rejectedExecution(runnable, executor);
    }
}

总结:

线程使用规则在一个项目里面是一个重要的版块,如何实现线程的合理调度和使用,关系到系统性能和系统的稳定。我们只能不断的优化性能,提高cpu的使用效率,提高线程在系统中的效率,保证系统稳定高可用是一个长期的探索。

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值