高併發環境下生成惟一流水號

高併發環境下生成惟一流水號的主要思路有兩種:java

  第一種是有一個控制全局的變量確保每一個流水號的惟一性;mysql

  第二種是每臺機器根據算法本身生成在系統中無衝突的流水號;算法

假設流水號的長度是128位(16字節);sql

第一種實現方法:(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位(全局變量);dom

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

採用字典樹測試是否有衝突;ide

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
相關文章
相關標籤/搜索