高併發環境下生成惟一流水號的主要思路有兩種: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; } }
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"); } }
……
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"); } }
線程池容量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); } }
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); }
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; }
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); }
這裏採用多線程測試的時候有一個問題,不一樣的線程之間是公用一個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); } }
測試結果:
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); }