导读: 线程在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的使用效率,提高线程在系统中的效率,保证系统稳定高可用是一个长期的探索。