Java并发编程-生成唯一序列号

标签: java 编程 唯一 | 发表时间:2016-02-03 03:27 | 作者:ivan19861025
出处:http://www.iteye.com

所用到的并发编程库

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

 

package com.league.idgenerate;

/**
 * 
 * ID生成器接口, 用于生成全局唯一的ID流水号
 * 
 * @author Ivan.Ma
 */
public interface IdGenerator {
	
	/**
	 * 生成下一个不重复的流水号
	 * @return
	 */
	String next();
	
}

 

package com.league.idgenerate;

/**
 * ID生成器的配置接口
 * @author Ivan.Ma
 */
public interface IdGeneratorConfig {
	
	/**
	 * 获取分隔符
	 * @return
	 */
	String getSplitString();
	
	/**
	 * 获取初始值
	 * @return
	 */
	int getInitial(); 
	
	/**
	 * 获取ID前缀
	 * @return
	 */
	String getPrefix();
	
	/**
	 * 获取滚动间隔, 单位: 秒
	 * @return
	 */
	int getRollingInterval();
	
}

 

package com.league.idgenerate;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * 默认的ID生成器, 采用前缀+时间+原子数的形式实现
 * 建议相同的配置采用同一个实例
 * @see IdGeneratorConfig
 * @author Ivan.Ma
 */
public class DefaultIdGenerator implements IdGenerator, Runnable{
	
	private String time;
	
	private AtomicInteger value;
	
	private static final DateFormat FORMATTER = new SimpleDateFormat("yyyyMMddHHmmss");
	
	private IdGeneratorConfig config;
	
	private Thread thread;
	
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	
	public DefaultIdGenerator(){
		config = new DefaultIdGeneratorConfig();
		time = DefaultIdGenerator.FORMATTER.format(new Date());
		value = new AtomicInteger(config.getInitial());
		
		thread = new Thread(this);
		thread.setDaemon(true);
		thread.start();
	}
	
	public DefaultIdGenerator(IdGeneratorConfig config){
		this.config = config;
		time = DefaultIdGenerator.FORMATTER.format(new Date());
		value = new AtomicInteger(config.getInitial());
		
		thread = new Thread(this);
		thread.setDaemon(true);
		thread.start();
	}
	
	@Override
	public String next() {
		lock.readLock().lock();
		StringBuffer sb = new StringBuffer(config.getPrefix()).append(config.getSplitString()).append(time).append(config.getSplitString()).append(value.getAndIncrement());
		lock.readLock().unlock();
		return sb.toString();
	}
	
	@Override
	public void run() {
		while (true){
			try {
				Thread.sleep(1000 * config.getRollingInterval());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			String now = DefaultIdGenerator.FORMATTER.format(new Date());
			if (!now.equals(time)){
				lock.writeLock().lock();
				time = now;
				value.set(config.getInitial());
				lock.writeLock().unlock();
			}
		}
	}
	
}

 

package com.league.idgenerate;

public class DefaultIdGeneratorConfig implements IdGeneratorConfig{

	@Override
	public String getSplitString() {
		return "";
	}

	@Override
	public int getInitial() {
		return 1;
	}

	@Override
	public String getPrefix() {
		return "";
	}

	@Override
	public int getRollingInterval() {
		return 1;
	}

}

 测试类, 该类主要演示如何使用及相关测试功能代码,如下:

package com.league.idgenerate;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

/**
 * 用法说明
 * @author Ivan.Ma
 */
public class TestStandaloneIdGenerator {
	
	@Test
	public void test1(){
		IdGenerator idGenerator = new DefaultIdGenerator();
		
		System.out.println("--------简单测试------------------");
		for (int i=0; i<100; i++){
			System.out.println(idGenerator.next());
		}
	}
	
	@Test
	public void test2(){
		IdGenerator idGenerator = new DefaultIdGenerator();
		
		//多线程测试
		System.out.println("--------多线程测试不重复------------------");
		Set<String> idSet = Collections.synchronizedSet(new HashSet<>());
		ExecutorService es = Executors.newFixedThreadPool(100);
		for (int i=0; i<2000000; i++){
			es.submit(() -> {
				String val = idGenerator.next();
				if (idSet.contains(val)){
					System.out.println("重复了: " + val);
				}else{
					idSet.add(val);
				}
			});
		}
		es.shutdown();
		System.out.println("启用顺序关闭");
		while(true){  
            if(es.isTerminated()){
                System.out.println("所有的子线程都结束了!");  
                break;
            }
            try {
            	System.out.println("子线程的任务还没运行完");
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        }  
		System.out.println("共生成: " + idSet.size() + "个");
	}
	
	@Test
	public void  test3(){
		//测试单机性能
		System.out.println("--------测试单线程性能------------------");
		IdGenerator idGenerator2 = new DefaultIdGenerator();
		long t1 = System.currentTimeMillis();
		int total = 10000000;
		for (int i=0; i<total; i++){
			idGenerator2.next();
		}
		System.out.println("单线程生成" + total + "个ID共耗时: " + (System.currentTimeMillis() - t1) + "ms");
	}
	
	//500个线程并发, 每个线程获取10000个ID
	@Test
	public void test4(){
		//测试多线程性能
		System.out.println("--------测试多线程性能------------------");
		ExecutorService es1 = Executors.newFixedThreadPool(500);
		IdGenerator idGenerator3 = new DefaultIdGenerator();
		long t1 = System.currentTimeMillis();
		for (int i=0; i<500; i++){
			es1.submit(() -> {
				int count = 0;
				while (count < 10000){
					idGenerator3.next();
					
					count++;
				}
			});
		}
		es1.shutdown();
		System.out.println("启用顺序关闭");
		while(true){  
            if(es1.isTerminated()){
                System.out.println("所有的子线程都结束了!");  
                break;
            }
            try {
            	System.out.println("子线程的任务还没运行完");
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        }  
		System.out.println("500线程,每个线程生成10000个序列号.共耗时: " + (System.currentTimeMillis() - t1) + " ms");
	}
	
	@Test
	public void test5(){
		System.out.println("--------测试生成的ID是否有时间滚动----------");
		IdGenerator idGenerator = new DefaultIdGenerator();
		for (int i=0; i<20; i++){
			String id = idGenerator.next();
			System.out.println(id);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Test
	public void test6(){
		System.out.println("--------ID生成器的特殊设置相关----------");
		IdGeneratorConfig config = new DefaultIdGeneratorConfig() {
			@Override
			public String getSplitString() {
				return "-";
			}
			@Override
			public int getInitial() {
				return 1000000;
			}
			@Override
			public String getPrefix() {
				return "NODE01";
			}
		};
		IdGenerator idGenerator = new DefaultIdGenerator(config);
		for (int i=0; i<20; i++){
			String id = idGenerator.next();
			System.out.println(id);
			try {
				Thread.sleep(1000 * 1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 运行结果如下:

--------简单测试------------------
201602031124321
201602031124322
201602031124323
201602031124324
201602031124325
201602031124326
201602031124327
201602031124328
201602031124329
2016020311243210
2016020311243211
2016020311243212
2016020311243213
2016020311243214
2016020311243215
2016020311243216
2016020311243217
2016020311243218
2016020311243219
2016020311243220
2016020311243221
2016020311243222
2016020311243223
2016020311243224
2016020311243225
2016020311243226
2016020311243227
2016020311243228
2016020311243229
2016020311243230
2016020311243231
2016020311243232
2016020311243233
2016020311243234
2016020311243235
2016020311243236
2016020311243237
2016020311243238
2016020311243239
2016020311243240
2016020311243241
2016020311243242
2016020311243243
2016020311243244
2016020311243245
2016020311243246
2016020311243247
2016020311243248
2016020311243249
2016020311243250
2016020311243251
2016020311243252
2016020311243253
2016020311243254
2016020311243255
2016020311243256
2016020311243257
2016020311243258
2016020311243259
2016020311243260
2016020311243261
2016020311243262
2016020311243263
2016020311243264
2016020311243265
2016020311243266
2016020311243267
2016020311243268
2016020311243269
2016020311243270
2016020311243271
2016020311243272
2016020311243273
2016020311243274
2016020311243275
2016020311243276
2016020311243277
2016020311243278
2016020311243279
2016020311243280
2016020311243281
2016020311243282
2016020311243283
2016020311243284
2016020311243285
2016020311243286
2016020311243287
2016020311243288
2016020311243289
2016020311243290
2016020311243291
2016020311243292
2016020311243293
2016020311243294
2016020311243295
2016020311243296
2016020311243297
2016020311243298
2016020311243299
20160203112432100
--------多线程测试不重复------------------
启用顺序关闭
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
所有的子线程都结束了!
共生成: 2000000个
--------测试单线程性能------------------
单线程生成10000000个ID共耗时: 1972ms
--------测试多线程性能------------------
启用顺序关闭
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
子线程的任务还没运行完
所有的子线程都结束了!
500线程,每个线程生成10000个序列号.共耗时: 1605 ms
--------测试生成的ID是否有时间滚动----------
201602031124431
201602031124432
201602031124433
201602031124434
201602031124435
201602031124436
201602031124437
201602031124438
201602031124439
2016020311244310
2016020311244311
201602031124441
201602031124442
201602031124443
201602031124444
201602031124445
201602031124446
201602031124447
201602031124448
201602031124449
--------ID生成器的特殊设置相关----------
NODE01-20160203112445-1000000
NODE01-20160203112445-1000001
NODE01-20160203112446-1000000
NODE01-20160203112447-1000000
NODE01-20160203112448-1000000
NODE01-20160203112449-1000000
NODE01-20160203112450-1000000
NODE01-20160203112452-1000000
NODE01-20160203112453-1000000
NODE01-20160203112454-1000000
NODE01-20160203112455-1000000
NODE01-20160203112456-1000000
NODE01-20160203112457-1000000
NODE01-20160203112458-1000000
NODE01-20160203112459-1000000
NODE01-20160203112500-1000000
NODE01-20160203112501-1000000
NODE01-20160203112502-1000000
NODE01-20160203112503-1000000
NODE01-20160203112504-1000000

 从该性能来看, 可以使用在高并发的场景, 欢迎大家来拍砖!

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 编程 唯一] 推荐:

Java并发编程-生成唯一序列号

- - 编程语言 - ITeye博客
package com.league.idgenerate; /** * * ID生成器接口, 用于生成全局唯一的ID流水号 * * @author Ivan.Ma */ public interface IdGenerator {. * 生成下一个不重复的流水号. package com.league.idgenerate; /** * ID生成器的配置接口 * @author Ivan.Ma */ public interface IdGeneratorConfig {.

Java编程规范

- - Web前端 - ITeye博客
本文档的编写从简,绝大多数内容以条款或者表格形式列出,不做过多的补充说明,代码格式规范遵循eclipse的默认编码规范要求. •    简单,易执行. 1.    名字含义要明确,做到见名知义,如: User,Role, UserManager. 2.    尽量使用英文名字作为变量名,如果要使用中文,请写上备注.

java编程风格指南

- - 行业应用 - ITeye博客
受不了的可以直接到以下网址查看. 作者:Hawstein 出处:http://hawstein.com/posts/google-java-style.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处.

面向GC的Java编程

- - 并发编程网 - ifeve.com
Java程序员在编码过程中通常不需要考虑内存问题,JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题. 以至于许多Java程序员认为,我只需要关心何时创建对象,而回收对象,就交给GC来做吧. 甚至有人说,如果在编程过程中频繁考虑内存问题,是一种退化,这些事情应该交给编译器,交给虚拟机来解决.

Java并发编程基础

- - 并发编程网 - ifeve.com
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力. 如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善. 现代的PC都有多个CPU或一个CPU中有多个核. 是否能合理运用多核的能力将成为一个大规模应用程序的关键. 进程是以独立于其他进程的方式运行的,进程间是互相隔离的.

java 并发编程 synchronized

- - Java - 编程语言 - ITeye博客
同步原语--synchronized. synchronized(class)很特别,它会让另一个线程在任何需要获取class做为monitor的地方等待.class与this做为不同的监视器可以同时使用,不存在一个线程获取了class,另一个线程就不能获取该class的一切实例.. ->线程各自获取monitor,不会有等待..

Java NIO编程的技巧和陷阱

- 小丑鱼 - 淘宝JAVA中间件团队博客
去年做的分享,一直上传slideshare失败,今天又试了下,成功了. 这个主题主要介绍Java NIO编程的技巧和陷阱,解读了一些NIO框架的源码,以及编写高性能NIO网络框架所需要注意的技巧和缺陷. 去年写了篇blog提供了pdf版本的下载,看这里.

JAVA接口编程的优点

- - ITeye博客
小弟刚工作两年文中所说都是通过自己工作中或做练习总结的经验,当然肯定不是那么准确,有不对之处望和谐指出,因为这个感觉完全是理论所以没有贴一段代码,有兴趣的就看看吧. 项目开发中绝对会碰到接口,但是为什么要使用接口成了绝大多数java程序员的疑问,为什么一定要写个没有实现任何功能的只有几个方名的类,可能也有一些人明白接口就是好,但好在哪里很难解释出来,我当初也是这样想的,但是当我一瞬间觉悟后发现这种想法很幼稚.

Java并发编程【1.2时代】

- - 并发编程网 - ifeve.com
         本文介绍了Java原生的多线程技术(1.2),通过详细介绍wait和notify相关的机制、基础的多线程技术以及基于这些技术的等待超时、线程间的通信技术和线程池高阶技术,最后通过一个基于线程池的简单文本web服务器—MollyServer,来阐明多线程带来好处. 通过介绍这些技术,展示了在没有使用Java并发包的时代(1.5-)是如何完成Java的多线程编程,为理解Java5提供了良好帮助.

Java 异步编程最佳实践

- - 鸟窝
最近异步编程非常流行, 主要是它能够在多核系统上提高吞吐率. 异步编程是一种编程方式,可以提高对UI的快速响应. Java中的异步编程模型提供了一致性的编程模型, 可以用来在程序中支持异步. 本文讨论了在使用Java执行异步操作应该遵循的最佳实践. 原文: Best Practices of Asynchronous Programming With Java.