日志解析LogParser类的工厂注册单例模式实现(线程安全)

 一、LogParserFactory的实现

1.为每个调用线程分配一个唯一的LogParser(日志解析)对象,以使在线程内部重复使用;以键值对<Thread, LogParser>形式注册到同一内存的Map中。

2.为每个调用对象(如:MR中的Mapper处理类)分配一个唯一的LogParser(日志解析)对象,以使在对象内部重复使用;以键值对<Object, LogParser>形式注册到同一内存的Map中。

注:经过测试,发现MapReduce任务是多进程单线程任务,即多个Map任务其实是多个进程,各进程间内存独立,所以不存在线程不安全的问题,也就此工厂实现类是多余的,除非开启MapReduce的多线程模式。

3.因为是多线程调用,所以存在线程不安全问题,需要使用 ConcurrentHashMap 来实现注册。(HashMap在put()时hash碰撞、扩容时都会发生线程不安全,导致注册内容丢失。)

4.使用反射机制创建LogParser对象。

5.提供的两个获取实例方法:

//简单单例模式
public static LogParser getInstance();
//按当前线程获取LogParser对象
public static LogParser getInstance(Thread thread);
//按当前调用者对象获取LogParser对象
public static LogParser getInstance(Object obj);

6.最后有两个测试用例:(注:可以尝试将Map改成HashMap后再试试,会发现注册数据丢失

//测试线程控制的方法
testThreadLogParser();
//测试对象控制的方法
testObjLogParser();

 7.完整代码实现:

注:日志解析类LogParser参看上篇博文:Apache服务器日志Log解析,也可自行实现简单的私有构造类做测试。

package com.etl.utls;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class LogParserFactory {
	
	//简单单例模式
	private static LogParser logParser = null;
	public static LogParser getInstance() {
		if (logParser != null) {
			return logParser;
		}
		else {
			try {
				//反射创建对象
				Class LogParserClass = Class.forName(LogParser.class.getName());
				Constructor constructor = LogParserClass.getDeclaredConstructor();
				constructor.setAccessible(true);
				LogParser logParser = (LogParser) constructor.newInstance();
				
				return logParser;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}
	}
	
	//日志解析对象按线程注册(一个线程对应一个解析对象)
	public static Map<Thread, LogParser> threadLogParserMap = new ConcurrentHashMap<Thread, LogParser>();
	
	//单利模式获取当前线程对应的日志解析对象
	public static LogParser getInstance(Thread thread) {
		if (threadLogParserMap.containsKey(thread)) {
			LogParser logParser = threadLogParserMap.get(thread);
			return logParser;
		}
		else {
			try {
				//反射创建对象
				Class LogParserClass = Class.forName(LogParser.class.getName());
				Constructor constructor = LogParserClass.getDeclaredConstructor();
				constructor.setAccessible(true);
				LogParser logParser = (LogParser) constructor.newInstance();
				//map插入
				threadLogParserMap.put(thread, logParser);
				
				return logParser;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}
	}
	
	public static void testThreadLogParser() {
		Thread thread = null;
		for (int i = 0; i < 5; i++) {
			
			thread = new Thread(new Runnable() {
				
				@Override
				public void run() {
					LogParser logParser = null;
					for (int i = 0; i < 2; i++) {
						//解析log日志
						logParser = LogParserFactory.getInstance(Thread.currentThread());
						logParser.parse(LogParser.LOG);
						//验证是否发生map.put()时的碰撞丢失(多线程时HashMap不安全)
						System.out.println("Current Thread: " + Thread.currentThread().getName()
						+ "\tCurrent LogParser Index: " + logParser.getIndex()
						+ "\tis equal: " + logParser.equals(
								LogParserFactory.threadLogParserMap.get(Thread.currentThread())));
					}
				}
				
			}, "Thread-" + i);
			thread.start();
		}
	}
	
	
	//日志解析对象按对象注册(一个调用对象对应一个解析对象)
	public static Map<Object, LogParser> objectLogParserMap = new ConcurrentHashMap<Object, LogParser>();
	
	//单利模式获取当前对象对应的日志解析对象
	public static LogParser getInstance(Object obj) {
		if (objectLogParserMap.containsKey(obj)) {
			LogParser logParser = objectLogParserMap.get(obj);
			return logParser;
		}
		else {
			try {
				//反射创建对象
				Class LogParserClass = Class.forName(LogParser.class.getName());
				Constructor constructor = LogParserClass.getDeclaredConstructor();
				constructor.setAccessible(true);
				LogParser logParser = (LogParser) constructor.newInstance();
				//map插入
				objectLogParserMap.put(obj, logParser);
				
				return logParser;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}
	}
	
	public static void testObjLogParser() {
		//调用LogParser对象的对象
		class MyObj {
			private String name;
			public MyObj(String name) {this.name = name;}
			public String getName() {return this.name;}
			public void driver() {
				LogParser logParser = null;
				//每个MyObj多次执行解析操作,重用解析对象
				for (int i = 0; i < 2; i++) {
					//解析log日志
					logParser = LogParserFactory.getInstance(this);
					logParser.parse(LogParser.LOG);
					//验证是否发生map.put()时的碰撞丢失(多线程时HashMap不安全)
					System.out.println("Current Object: " + this.getName()
					+ "\tCurrent LogParser Index: " + logParser.getIndex()
					+ "\tis equal: " + logParser.equals(LogParserFactory.objectLogParserMap.get((Object) this)));
				}
			}
		}
		//创建5个线程,每个线程发起两个不同的MyObj对象操作LogParser
		Thread thread = null;
		for (int i = 0; i < 5; i++) {
			thread = new Thread(new Runnable() {
				
				@Override
				public void run() {
					for (int i = 0; i < 2; i++) {
						new MyObj(Thread.currentThread().getName() + "-obj-" + i).driver();
					}
				}
				
			}, "Thread-" + i);
			thread.start();
		}
	}
	
	public static void main(String[] args) {
		
//		testThreadLogParser();
//		testObjLogParser();
	}

}

二、参考资料

1.HashMap为什么线程不安全(hash碰撞与扩容导致)  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值