Java知识总结----事件处理机制(九)

      在上一篇文章中,跟大家介绍了队列的使用,在基于数据库的队列的是实现中,提到了可以使用事件处理机制类进行队列数据的处理。今天就来简单看看事件处理机制是怎么使用的。

      首先我们需要一个事件的实体类:Event

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 消息实体类
 */
public class Event {
	//消息体
	private Object data;
	//消息类型
	private Integer eventType;
	//重试次数
	private AtomicInteger tryCount = new AtomicInteger(0);
	//消息等待发送时间
	private long waitTime;
	//消息标识
	private String UUID;
	
	public Object getData() {
		return data;
	}
	public void setData(Object data) {
		this.data = data;
	}
	public Integer getEventType() {
		return eventType;
	}
	public void setEventType(Integer eventType) {
		this.eventType = eventType;
	}
	public AtomicInteger getTryCount() {
		return tryCount;
	}
	public void setTryCount(AtomicInteger tryCount) {
		this.tryCount = tryCount;
	}
	public long getWaitTime() {
		return waitTime;
	}
	public void setWaitTime(long waitTime) {
		this.waitTime = waitTime;
	}
	public String getUUID() {
		return UUID;
	}
	public void setUUID(String uUID) {
		UUID = uUID;
	}
	
}

接着是事件处理结果类:EventResult

import java.io.Serializable;

/**
 * 消息处理结果
 */
public class EventResult implements Serializable {
	private static final long serialVersionUID = 560051808572760660L;
	//是否成功
	private boolean isSuccess;
	//结果信息
	private String resultMsg;
	public boolean isSuccess() {
		return isSuccess;
	}
	
	public void setSuccess(boolean isSuccess) {
		this.isSuccess = isSuccess;
	}
	public String getResultMsg() {
		return resultMsg;
	}
	public void setResultMsg(String resultMsg) {
		this.resultMsg = resultMsg;
	}
	
}

对于事件来说,不同的事件类型可以有不同的事件处理类:基类:EventHandler,实现类:OneEventHandler

/**
 * 处理队列中的各种事件
 */
public interface EventHandler {
	/**
	 * 处理事件,返回处理结果
	 * @param event
	 */
	public EventResult handleEvent(final Event event);
}
import com.demo.event.Event;
import com.demo.event.EventHandler;
import com.demo.event.EventResult;

/**
 * 具体的事件处理类,根据不同的事件类型,可以有多种处理类
 */
public class OneEventHandler implements EventHandler {

	public EventResult handleEvent(Event event) {
		return null;
	}

}
下面,我们需要一个类来执行事件任务:EventWorker

import java.util.concurrent.Callable;

/**
 * 用于执行事件任务
 * @author lizhiyang
 *
 */
public class EventWorker implements Callable<EventResult> {

	//要处理的事件
	private Event event;
	//使用的事件处理器
	private EventHandler eventHandler;
	
	public EventWorker(Event event, EventHandler eventHandler) {
		this.event = event;
		this.eventHandler = eventHandler;
	}
	
	/**
	 * 调用事件处理器处理事件
	 */
	public EventResult call() throws Exception {
		if(event == null) {
			return null;
		}
		if(eventHandler == null) {
			return null;
		}
		EventResult eventResult = null;
		try {
			eventResult = eventHandler.handleEvent(event);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return eventResult;
	}

}
在事件处理的时候,需要有一个管理类,去添加事件:EventManager

import com.demo.event.Event;

public interface EventManager {

	/**
	 * 添加异步处理事件
	 * @param event
	 */
	public void addEvent(final Event event);
	/**
	 * 添加同步处理事件
	 * @param event
	 */
	public void addEventSyn(Event event);
}

分为两种,同时处理事件和异步处理事件,在第一次添加的时候都使用同步处理事件,如果时间处理失败或出现其他原因时,可以再次添加异步事件进行再次处理。

同步处理在EventMangerImpl中:

import java.util.Calendar;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.demo.event.Event;
import com.demo.event.EventHandler;
import com.demo.event.EventResult;
import com.demo.event.EventScheduled;
import com.demo.event.EventWorker;
import com.demo.event.handler.OneEventHandler;

public class EventManagerImpl implements EventManager {

private final static Logger logger = LoggerFactory.getLogger(EventManagerImpl.class);
	
	/**  开启队列调度线程 */
	private EventScheduled eventScheduled;
	
	/**  线程池管理 */
	/**
	 * spring 中的配置:
	 * 	<bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">                  
	    <property name="corePoolSize" value="25" />          
	    <!-- 最大线程数,默认为Integer.MAX_VALUE -->          
	    <property name="maxPoolSize" value="400" />          
	    <!-- 队列最大长度 -->          
	    <property name="queueCapacity" value="1000" />          
	    <!-- 线程池维护线程所允许的空闲时间-->          
	    <property name="keepAliveSeconds" value="200" />          
	    <!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->          
	    <property name="rejectedExecutionHandler">              
	        <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->              
	        <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />          
	    </property>          
    </bean> 
	 */
	private ThreadPoolTaskExecutor threadPoolTaskExecutor;
	
	/**
	 *  初始化异步调度线程
	 */
	public void init(){
		//构造调度任务
		eventScheduled = new EventScheduled();
		//启动调度线程
		eventScheduled.start();
	}

	/**
	 * 添加新的事件到队列中
	 * @param event
	 */
	public void addEvent(final Event event){
		if(!validateEvent(event)){
			if(logger.isDebugEnabled()){
			    logger.debug("addEvent event is illega");
			}
			return;
		}
		eventScheduled.addEvent(event);
	}
	
	public void addEventSyn(Event event) {
		if(!validateEvent(event)){
			if(logger.isDebugEnabled()){
			    logger.debug("addEventSyn event is illega");
			}
			return;
		}
		EventHandler eventHandle = new OneEventHandler();
		Future<EventResult>  future = null;
		try {
			future = threadPoolTaskExecutor.getThreadPoolExecutor().submit(new EventWorker(event,eventHandle));
			if(future != null) {
				EventResult eventResult = future.get();
				if(eventResult == null){
					if(logger.isDebugEnabled()){
						logger.debug("submit eventResult return null");
					}
					return;
				} 
				if(logger.isDebugEnabled()) {
					logger.debug("event submit :" + eventResult.toString());
				}
				//没有发送成功等待下次发送
				if(!eventResult.isSuccess()){
					// 暂时不用线程调度,因为缓存的问题
/*					int tryCount = event.getTryCount().intValue();
					event.setWaitTime(QueueRuleUtil.getNextConsumeSecond(tryCount,0));
					addEvent(event);*/
				} else {
					// 发送成功但是可能数据操作没有成功,或者是其他原因,对于这些数据采取线程调度处理,不需要马上处理
					event.setEventType(0);
					//一分钟后处理
					Calendar calendar = Calendar.getInstance();
					calendar.add(Calendar.MINUTE, 1);
					event.setWaitTime(calendar.getTime().getTime());
					addEvent(event);
				}
			}
		} catch (InterruptedException e) {
			logger.error(this.getClass() + "event submit Interrupted exception :",e);
			future.cancel(true);// 中断执行此任务的线程  
		} catch (ExecutionException e) {
			logger.error(this.getClass() + "event submit Execution exception:" ,e);
			 future.cancel(true);// 中断执行此任务的线程  
		}
	}
	
	/**
	 * 验证事件是否有效
	 * 
	 * @param event
	 * @return boolean
	 */
	private boolean validateEvent(Event event) {
		if (event == null) {
			if (logger.isDebugEnabled()) {
				logger.debug("addEventSyn is must not be null");
			}
			return false;
		}
		if (StringUtils.isBlank(event.getUUID()) || event.getData() == null
				|| event.getEventType() == null) {
			if (logger.isDebugEnabled()) {
				logger.debug("event request params is must not be null");
			}
			return false;
		}
		return true;
	}
		
	public void setThreadPoolTaskExecutor(
			ThreadPoolTaskExecutor threadPoolTaskExecutor) {
		this.threadPoolTaskExecutor = threadPoolTaskExecutor;
	}

}

异步处理时,需要有调度器进行处理:EventScheduled

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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

import com.demo.event.handler.OneEventHandler;

/**
 * 消息调度线程,主要用于消息的定时发送
 * 
 * 消息的异步处理过程
 *
 */
public class EventScheduled {
	
	private Logger logger = LoggerFactory.getLogger(EventScheduled.class);
	/**
	 * 调度线程池,用于重发数据
	 */
	private ScheduledExecutorService scheduledExecutorService;
	
	/**  采用无界队列	 */
	private final static ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
	
	/**
	 * 单个cpu的线程数
	 */
	private final int  POOL_SIZE = 5;
	
	 /**  队列监听等待时间 */
    private long waitTime = 200;
    
    /**
     * 添加异步事件
     * @param event
     */
    public void addEvent(Event event){
		if (event != null) {
			eventQueue.add(event);
		}
    }
    
    /**
     * 构造带不带缓存的客户端
     */
    public EventScheduled(){
    	//初始化ScheduledExecutorService 服务
		scheduledExecutorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * POOL_SIZE);
    }
    
    
    /**
	 * 阻塞监听线程,只负责执行需要调度的消息
	 */
	public void start(){
		logger.info("EventScheduled start");
		new  Thread() {
			public void run() {
				while(true){
					try{
		            	CountDownLatch latch = new CountDownLatch(1);
		                latch.await(waitTime,TimeUnit.MILLISECONDS);
						Event event=eventQueue.poll();//线程安全
						if(event==null){
							continue;
						}
						//根据消息类型构造不同的事件处理类
						EventHandler eventHandle = new OneEventHandler();
						Future<EventResult>  future = null;
						try {
							future = scheduledExecutorService.schedule(new EventWorker(event,eventHandle), event.getWaitTime(), TimeUnit.SECONDS);
							if(future != null) {
								EventResult result = future.get();
								if(result == null){
									if(logger.isDebugEnabled()){
										logger.debug("event scheduled return null");
									}
									return;
								} 
								if(logger.isDebugEnabled()) {
									logger.debug("event scheduled :" + result.toString());
								}
							}
						} catch (InterruptedException e) {
							logger.error("event scheduled Interrupted exception :",e);
							 future.cancel(true);// 中断执行此任务的线程  
						} catch (ExecutionException e) {
							logger.error("event scheduled Execution exception:" ,e);
							 future.cancel(true);// 中断执行此任务的线程  
						}
					}catch(Throwable e){
						  logger.error("scheduled Exception:",e);
					}
				}
			}
		
		}.start();
	 }
}

以上就是事件处理机制的代码部分。对于事件处理机制来说,我本人的理解并不是特别的透彻,这里就不进行详细说明了,只是把示例代码贴出,仅供大家参考。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 (2)建模简单:通过使用线程可以讲复杂并且异步的工作流进一步分解成一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置交互 (3)简化异步事件的处理:服务器应用程序在接受来自多个远程客户端的请求时,如果为每个连接都分配一个线程并且使用同步IO,就会降低开发难度 (4)用户界面具备更短的响应时间:现代GUI框架中大都使用一个事件分发线程(似于中断响应函数)来替代主事件循环,当用户界面用有事件发生时,在事件线程中将调用对应的事件处理函数(似于中断处理函数) 线程的风险 线程安全性:永远不发生糟糕的事情 活跃性问题:某件正确的事情迟早会发生 问题:希望正确的事情尽快发生 服务时间过长 响应不灵敏 吞吐率过低 资源消耗过高 可伸缩性较低 线程的应用场景 Timer 确保TimerTask访问的对象本身是线程安全的 Servlet和JSP Servlet本身要是线程安全的 正确协同一个Servlet访问多个Servlet共享的信息 远程方法调用(RMI) 正确协同多个对象中的共享状态 正确协同远程对象本身状态的访问 Swing和AWT 事件处理器与访问共享状态的其他代码都要采取线程安全的方式实现 框架通过在框架线程中调用应用程序代码将并发性引入应用程序,因此对线程安全的需求在整个应用程序中都需要考虑 基础知识 线程安全性 定义 当多个线程访问某个时,这个始终能表现出正确的行为,那么就称这个是线程安全的 无状态对象一定是线程安全的,大多数Servlet都是无状态的 原子性 一组不可分割的操作 竞态条件 基于一种可能失效的观察结果来做出判断或执行某个计算 复合操作:执行复合操作期间,要持有锁 锁的作用 加锁机制、用锁保护状态、实现共享访问 锁的不恰当使用可能会引起程序性能下降 对象的共享使用策略 线程封闭:线程封闭的对象只能由一个线程拥有并修改 Ad-hoc线程封闭 栈封闭 ThreadLocal 只读共享:不变对象一定是线程安全的 尽量将域声明为final型,除非它们必须是可变的 分 不可变对象 事实不可变对象 线程安全共享 封装有助于管理复杂度 线程安全的对象在其内部实现同步,因此多个接口可以通过公有接口来进行访问 保护对象:被保护的对象只能通过特定的锁来访问 将对象封装到线程安全对象中 由特定锁保护 保护对象的方法 对象的组合 设计线程安全的 实例封闭 线程安全的委托 委托是创建线程安全的最有效策略,只需要让现有的线程安全管理所有的状态 在现有线程安全中添加功能 将同步策略文档化 基础构建模块 同步容器 Vector Hashtable 实现线程安全的方式 将状态封装起来,对每个公有方法都进行同步 存在的问题 复合操作 修正方式 客户端加锁 迭代器 并发容器 ConcurrentHashMap 用于替代同步且基于散列的Map CopyOnWriteArrayList 用于在遍历操作为主要操作的情况下替代同步的List Queue ConcurrentLinkedQueue *BlockingQueue 提供了可阻塞的put和take方法 生产者-消费者模式 中断的处理策略 传递InterruptedException 恢复中断,让更高层的代码处理 PriorityQueue(非并发) ConcurrentSkipListMap 替代同步的SortedMap ConcurrentSkipListSet 替代同步的SortedSet Java 5 Java 6 同步工具 闭锁 *应用场景 (1)确保某个计算在其需要的所有资源都被初始化后才能继续执行 (2)确保某个服务在其所依赖的所有其他服务都已经启动之后才启动 (3)等待知道某个操作的所有参与者都就绪再继续执行 CountDownLatch:可以使一个或多个线程等待一组事件发生 FutureTask *应用场景 (1)用作异步任务使用,且可以使用get方法获取任务的结果 (2)用于表示一些时间较长的计算 状态 等待运行 正在运行 运行完成 使用Callable对象实例化FutureTask 信号量(Semaphore) 用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量 管理者一组虚拟的许可。acquire获得许可(相当于P操作),release释放许可(相当于V操作) 应用场景 (1)二值信号量可用作互斥体(mutex) (2)实现资源池,例如数据库连接池 (3)使用信号量将任何一种容器变成有界阻塞容器 栅栏 能够阻塞一组线程直到某个事件发生 栅栏和闭锁的区别 所有线程必须同时到达栅栏位置,才能继续执行 闭锁用于等待事件,而栅栏用于等待线程 栅栏可以重用 形式 CyclicBarrier 可以让一定数量的参与线程反复地在栅栏位置汇集 应用场景在并行迭代算法中非常有用 Exchanger 这是一种两方栅栏,各方在栅栏位置上交换数据。 应用场景:当两方执行不对称的操作(读和取) 线程池 任务与执行策略之间的隐形耦合 线程饥饿死锁 运行时间较长的任务 设置线程池的大小 配置ThreadPoolExecutor 构造参数 corePoolSize 核心线程数大小,当线程数= corePoolSize的时候,会把runnable放入workQueue中 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了” keepAliveTime 保持存活时间,当线程数大于corePoolSize的空闲线程能保持的最大时间。 workQueue 保存任务的阻塞队列 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务 threadFactory 创建线程的工厂 handler 拒绝策略 unit 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值 线程的创建与销毁 管理队列任务 饱和策略 AbortPolicy DiscardPolicy DiscardOldestPolicy CallerRunsPolicy 线程工厂 在调用构造函数后再定制ThreadPoolExecutor 扩展 ThreadPoolExecutor afterExecute(Runnable r, Throwable t) beforeExecute(Thread t, Runnable r) terminated 递归算法的并行化 构建并发应用程序 任务执行 在线程中执行任务 清晰的任务边界以及明确的任务执行策略 任务边界 大多数服务器以独立的客户请求为界 在每个请求中还可以发现可并行的部分 任务执行策略 在什么(What)线程中执行任务? 任务按照什么(What)顺序执行(FIFO、LIFO、优先级)? 有多少个(How Many)任务能并发执行? 在队列中有多少个(How Many)任务在等待执行? 如果系统由于过载而需要拒绝一个任务,那么应该选择哪一个(Which)任务?另外,如何(How)通知应用程序有任务被拒绝? 在执行一个任务之前或之后,应该进行什么(What)动作? 使用Exector框架 线程池 newFixedThreadPool(固定长度的线程池) newCachedThreadPool(不限规模的线程池) newSingleThreadPool(单线程线程池) newScheduledThreadPool(带延迟/定时的固定长度线程池) 具体如何使用可以查看JDK文档 找出可利用的并行性 某些应用程序中存在比较明显的任务边界,而在其他一些程序中则需要进一步分析才能揭示出粒度更细的并行性 任务的取消和关闭 任务取消 停止基于线程的服务 处理非正常的线程终止 JVM关闭 线程池的定制化使用 任务和执行策略之间的隐性耦合 线程池的大小 配置ThreadPoolExecutor(自定义的线程池) 此处需要注意系统默认提供的线程池是如何配置的 扩展ThreadPoolExector GUI应用程序探讨 活跃度(Liveness)、性能、测试 避免活跃性危险 死锁 锁顺序死锁 资源死锁 动态的锁顺序死锁 开放调用 在协作对象之间发生的死锁 死锁的避免与诊断 支持定时的显示锁 通过线程转储信息来分析死锁 其他活跃性危险 饥饿 要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级。 糟糕的响应性 如果由其他线程完成的工作都是后台任务,那么应该降低它们的优先级,从而提高前台程序的响应性。 活锁 要解决这种活锁问题,需要在重试机制中引入随机性(randomness)。为了避免这种情况发生,需要让它们分别等待一段随机的时间 性能与可伸缩性 概念 运行速度(服务时间、延时) 处理能力(吞吐量、计算容量) 可伸缩性:当增加计算资源时,程序的处理能力变强 如何提升可伸缩性 Java并发程序中的串行,主要来自独占的资源锁 优化策略 缩

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值