• 高并发环境下生成唯一流水号


    高并发环境下生成唯一流水号的主要思路有两种:

      第一种是有一个控制全局的变量确保每个流水号的唯一性;

      第二种是每台机器根据算法自己生成在系统中无冲突的流水号;

    假设流水号的长度是128位(16字节);

    第一种实现方法:(1)采用数据库的自增主键确保唯一性;

    Database.java

    package mine;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class Database {
        static String serialNumber;
        static String username="root";
        static String pwd = "123";
        static String url = "jdbc:mysql://192.168.1.6:3306/serialnumber"; 
        static String driver = "org.gjt.mm.mysql.Driver";
        private Connection con;
        private Statement statement;
        public static void main(String[] args){
            serialNumber =new Database().getSerialNumber();
            System.out.println(serialNumber);
        }
        private void start(){
            try {
                Class.forName( driver );
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                System.out.println("error in loading driver.");
            }
            
            long time=10000;
            while((con)==null&&time>0){//设置超时时间10s
                try {
                    Thread.sleep(100);
                    time-=100;    
                    con = DriverManager.getConnection(url,username,pwd);
                }catch(Exception e){}
            }
            time=1000;
            while((statement)==null&&time>0){
                try{
                    Thread.sleep(100);
                    time-=100;    
                    statement = con.createStatement();
                }catch(Exception e){}
            }           
        }
        private void close(){
            try {
                if(statement!=null)
                    statement.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("error in close statement.");
            } 
            try {
                if(con!=null)
                    con.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                System.out.println("error in close connection.");
            }
        }
        public String getSerialNumber(){
            start();
            String str="";
            long time =System.currentTimeMillis();
            try{
                statement.execute("insert serialnumber(time) values("+time+")");
                ResultSet re = statement.executeQuery("select NO from serialnumber where time="+time+";");
                if(re.next()){
                    str=re.getString(1);
                }
                re.close();
            }catch(Exception e){}
            finally{
                close();
            }
            return ""+time+str;
        } 
    }
    View Code

    ThreadTest.java,线程池容量为100(mysql的默认连接数是100);每个线程的连接超时时间为10秒,启动10000个线程;

    运行时间:34s

    package mine;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    class Client extends Thread{
        private String serialNumber="";
        private String ID="";
        static int connect=0;
        Client(String id){
            this.ID=id;
        }
        public void run(){
            //connect++;
            new Database().getSerialNumber();
            //System.out.println("thread "+ID+" run ……connect:"+connect);
            //connect--;
        }
        public String getSerialNumber(){
            return serialNumber;
        }
        public String getID(){
            return ID;
        }
    }
    
    public class ThreadTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            long time0=System.currentTimeMillis();
            ExecutorService executor = Executors.newFixedThreadPool(100);//creating a pool of 5 threads  
            for (int i = 0; i < 10000; i++) {  
                Thread client = new Client("" + i);  
                executor.execute(client);//calling execute method of ExecutorService  
            }  
            executor.shutdown();  
            while (!executor.isTerminated()) {} 
            int time =((int)(System.currentTimeMillis()-time0))/1000;
            System.out.println("Finished all threads time:"+time+"s");
        }
    
    }
    View Code
    ……
    Finished all threads time:34s

    (2)采用全局变量加锁的方式确保唯一性;流水号为20位十进制数:13位表示时间的数+7位(全局变量);

    package mine;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    class GSerialNumber extends Thread{
        private static Object lock=new Object();
        private static long golbal=0;
        private String SerialNumber="";
        public String getSerialNumber(){
            long temp;
            synchronized(lock){
                temp=golbal++;
            }
            SerialNumber=String.format("%013d%07d",System.currentTimeMillis(),temp);
            return SerialNumber;
        }
        public void run(){
            String str=getSerialNumber();
        }
    }
    public class GlobalLock {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            long time0=System.currentTimeMillis();
            int n =10000;
            ExecutorService executor =Executors.newFixedThreadPool(n);
            for(int i=0;i<100*n;i++){
                Thread thread = new GSerialNumber();
                executor.execute(thread);
            }
            executor.shutdown();
            while(!executor.isTerminated()){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {}
            }
            long time =(int)((System.currentTimeMillis()-time0));
            System.out.println("Time:"+time+"ms");
        }
    
    }
    View Code
    线程池容量10000;线程数:1000000;
    
    运行时间:
    
    Time:5107ms

    采用字典树测试是否有冲突;

    package mine;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import util.Trie;
    
    class GSerialNumber extends Thread{
        private static Object lock=new Object();
        private static long golbal=0;
        private String SerialNumber="";
        public String getSerialNumber(){
            long temp;
            synchronized(lock){
                temp=golbal++;
            }
            SerialNumber=String.format("%013d%07d",System.currentTimeMillis(),temp);
            return SerialNumber;
        }
        static Trie trie=new Trie(); 
        static Object tlock = new Object();
        public void run(){
            String str=getSerialNumber();
            synchronized(tlock){
                trie.insert(str);
            }
        }
    }
    public class GlobalLock {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            long time0=System.currentTimeMillis();
            int n =10000;
            ExecutorService executor =Executors.newFixedThreadPool(n);
            for(int i=0;i<100*n;i++){
                Thread thread = new GSerialNumber();
                executor.execute(thread);
            }
            executor.shutdown();
            while(!executor.isTerminated()){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {}
            }
            long time =(int)((System.currentTimeMillis()-time0));
            System.out.println("Time:"+time+"ms");
            System.out.println("冲突数:"+new GSerialNumber().trie.count);
        }
    
    }
    View Code
    create trie
    Time:5755ms
    冲突数:0

     第二种方案(独自生成流水号):

    (1)采用MAC地址+System.currentTimeMillis()+System.nanoTime();测试线程池容量为100,线程数为10000

        public static String method2(){
            NetworkInterface netInterface;
            long mac=0;
            try {
                netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
                mac =new BigInteger(netInterface.getHardwareAddress()).longValue();//.toString(10);
            }catch (SocketException | UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String str = String.format("%039d%013d%013d",mac,System.currentTimeMillis(),System.nanoTime());
            return encryp(str);
        }
    View Code
    create trie
    冲突数:10

    (2)采用MAC地址+System.currentTimeMillis()+Math.Random()*1000;测试线程池容量为100,线程数为10000

        public static String method3(){
            NetworkInterface netInterface;
            long mac=0;
            try {
                netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
                mac =new BigInteger(netInterface.getHardwareAddress()).longValue();
            }catch (SocketException | UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String str = String.format("%039d%013d%04d",mac,System.currentTimeMillis(),(int)(Math.random()*1000));
            return str;
        }
    View Code
    create trie
    冲突数:1

    (3)采用MAC地址+PID+System.currentTimeMillis();Thread(10);

        public static String method4(){
            NetworkInterface netInterface;
            long mac=0;
            try {
                netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
                mac =new BigInteger(netInterface.getHardwareAddress()).longValue();
            }catch (SocketException | UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String name = ManagementFactory.getRuntimeMXBean().getName();       
            // get pid    
            String pid = name.split("@")[0];
            String str = String.format("%039d%s%013d",mac,pid,System.currentTimeMillis());
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return encryp(str);
        }
    View Code

    这里采用多线程测试的时候有一个问题,不同的线程之间是公用一个pid,这样就会产生冲突;为此测试时我们应该把线程id也附加上;测试线程池容量为100,线程数为10000

    测试代码:

    package mine;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import util.Trie;
    
    class SerialCreate extends Thread{
        static int ID=0;
        int id=ID++;//这里线程不安全,不能这么用!
        static Trie tries = new Trie();
        static Object lock = new Object();
        public void run(){
            String str=new SerialNumber().method4()+String.format("%06d", id);//加入线程id
            synchronized (lock) {
                tries.insert(str);    
            }
        }
    }
    
    public class SerialNumberTest {
        public static void main(String[] args){
            int n =100;
            ExecutorService execupool = Executors.newFixedThreadPool(n);
            for(int i=0;i<100*n;i++){
                Thread serial = new SerialCreate();
                execupool.execute(serial);
            }
            execupool.shutdown();
            while(!execupool.isTerminated()){}
            System.out.println("冲突数:"+new SerialCreate().tries.count);
        }
    }
    View Code

    测试结果:

    create trie
    冲突数:0

    由于账单号是128位的,而上面的生成算法有的产生字符串表示的数字会超过128位,这样就需要用MD5算法加密散列成128位的数字;

    转换函数:encrpy()

        public static String encryp(String pwd){
            byte[] message=null;
            message = pwd.getBytes();
            MessageDigest md=null;
            try {
                md = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            byte[] encrypwd =md.digest(message);
            BigInteger bigInteger = new BigInteger(1, encrypwd);
            return bigInteger.toString(10);
        }
    View Code
  • 相关阅读:
    在HTML文件中加载js
    HTML表单的问题
    HTML自动换行的问题
    HTML的结束标签问题
    数据库基本知识
    PHP中数据库的连接
    2014年7月
    3.2版本视频讲解知识点
    2014年7月
    选项卡
  • 原文地址:https://www.cnblogs.com/yuanzhenliu/p/5720588.html
Copyright © 2020-2023  润新知