From e4ba696642cc145898c757c12cbc096c4b5592f0 Mon Sep 17 00:00:00 2001 From: feijianjun <–feijianjun_ext@cop.cmsoft.com.cn> Date: Mon, 26 Oct 2020 10:07:04 +0800 Subject: [PATCH] =?UTF-8?q?Java=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=9D=83=E5=A8=81=E6=8C=87=E5=8D=97=E4=BB=A3=E7=A0=811-?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=9C=AC=E5=9C=B0=EF=BC=8C=E5=88=86=E6=AE=B5?= =?UTF-8?q?map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fjj/pa/cas/BailoutFuture.java | 18 ++ src/main/java/com/fjj/pa/cas/BailoutMain.java | 181 +++++++++++++++++ src/main/java/com/fjj/pa/cas/TaxCallable.java | 111 +++++++++++ .../com/fjj/pa/cas/TaxPayerBailoutDB.java | 17 ++ .../com/fjj/pa/cas/TaxPayerBailoutDbImpl.java | 46 +++++ .../java/com/fjj/pa/cas/TaxPayerRecord.java | 36 ++++ .../java/com/fjj/pa/resize/BailoutFuture.java | 18 ++ .../java/com/fjj/pa/resize/BailoutMain.java | 182 ++++++++++++++++++ .../java/com/fjj/pa/resize/StateAndId.java | 16 ++ .../java/com/fjj/pa/resize/TaxCallable.java | 118 ++++++++++++ .../com/fjj/pa/resize/TaxPayerBailoutDB.java | 17 ++ .../fjj/pa/resize/TaxPayerBailoutDbImpl.java | 68 +++++++ .../com/fjj/pa/resize/TaxPayerRecord.java | 36 ++++ .../com/fjj/pa/segment/BailoutFuture.java | 18 ++ .../java/com/fjj/pa/segment/BailoutMain.java | 182 ++++++++++++++++++ .../java/com/fjj/pa/segment/StateAndId.java | 16 ++ .../java/com/fjj/pa/segment/TaxCallable.java | 119 ++++++++++++ .../com/fjj/pa/segment/TaxPayerBailoutDB.java | 17 ++ .../fjj/pa/segment/TaxPayerBailoutDbImpl.java | 69 +++++++ .../com/fjj/pa/segment/TaxPayerRecord.java | 36 ++++ .../com/fjj/pa/threadlocal/BailoutFuture.java | 18 ++ .../com/fjj/pa/threadlocal/BailoutMain.java | 181 +++++++++++++++++ .../com/fjj/pa/threadlocal/TaxCallable.java | 117 +++++++++++ .../fjj/pa/threadlocal/TaxPayerBailoutDB.java | 17 ++ .../pa/threadlocal/TaxPayerBailoutDbImpl.java | 46 +++++ .../fjj/pa/threadlocal/TaxPayerRecord.java | 36 ++++ 26 files changed, 1736 insertions(+) create mode 100644 src/main/java/com/fjj/pa/cas/BailoutFuture.java create mode 100644 src/main/java/com/fjj/pa/cas/BailoutMain.java create mode 100644 src/main/java/com/fjj/pa/cas/TaxCallable.java create mode 100644 src/main/java/com/fjj/pa/cas/TaxPayerBailoutDB.java create mode 100644 src/main/java/com/fjj/pa/cas/TaxPayerBailoutDbImpl.java create mode 100644 src/main/java/com/fjj/pa/cas/TaxPayerRecord.java create mode 100644 src/main/java/com/fjj/pa/resize/BailoutFuture.java create mode 100644 src/main/java/com/fjj/pa/resize/BailoutMain.java create mode 100644 src/main/java/com/fjj/pa/resize/StateAndId.java create mode 100644 src/main/java/com/fjj/pa/resize/TaxCallable.java create mode 100644 src/main/java/com/fjj/pa/resize/TaxPayerBailoutDB.java create mode 100644 src/main/java/com/fjj/pa/resize/TaxPayerBailoutDbImpl.java create mode 100644 src/main/java/com/fjj/pa/resize/TaxPayerRecord.java create mode 100644 src/main/java/com/fjj/pa/segment/BailoutFuture.java create mode 100644 src/main/java/com/fjj/pa/segment/BailoutMain.java create mode 100644 src/main/java/com/fjj/pa/segment/StateAndId.java create mode 100644 src/main/java/com/fjj/pa/segment/TaxCallable.java create mode 100644 src/main/java/com/fjj/pa/segment/TaxPayerBailoutDB.java create mode 100644 src/main/java/com/fjj/pa/segment/TaxPayerBailoutDbImpl.java create mode 100644 src/main/java/com/fjj/pa/segment/TaxPayerRecord.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/BailoutFuture.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/BailoutMain.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/TaxCallable.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDB.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDbImpl.java create mode 100644 src/main/java/com/fjj/pa/threadlocal/TaxPayerRecord.java diff --git a/src/main/java/com/fjj/pa/cas/BailoutFuture.java b/src/main/java/com/fjj/pa/cas/BailoutFuture.java new file mode 100644 index 0000000..1088a90 --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/BailoutFuture.java @@ -0,0 +1,18 @@ +package com.fjj.pa.cas; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 14:58 + */ +@Data +@AllArgsConstructor +public class BailoutFuture { + + private double interationsPerSecond; + private long recordsAdded, recordsRemoced, nullCounter; +} diff --git a/src/main/java/com/fjj/pa/cas/BailoutMain.java b/src/main/java/com/fjj/pa/cas/BailoutMain.java new file mode 100644 index 0000000..674ccc3 --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/BailoutMain.java @@ -0,0 +1,181 @@ +package com.fjj.pa.cas; + + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.*; + +public class BailoutMain { + final public static int TEST_TIME = 120 * 1000; + final public static Random random = new Random(Thread.currentThread().getId()); + private static char[] alphabet = {'a', 'b', 'c', 'd', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + private static String[] states = {"Alabama", "Alaska", "Arizona", + "Arkansas", "California", "Colorado", "Connecticut", + "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", + "Illinois", "Indiana", "Iowa", + "Kansas", "Kentucky", + "Louisiana", "Maine", "Maryland", "Massachusetts", + "Michigan", "Minnesota", "Mississippi, Missouri", "Montana", "Nebraska", + "Nevada ", "New Hampshire", "New Jersey, New Mexiew York", + "North Carolina", "North Dakota", "Ohioklahoma0regon", "Pennsylvania", + "Rhode Island", + "South Carolina", "South Dakota", "Tennessee", "Texas", + "Utah Vermont", "Virginia", "Washington", + "West Virgina", + "Wisconsin", "Wyoming"}; + + public static void main(String[] args) { + final int numberOfThreads = Runtime.getRuntime().availableProcessors(); + final int dbSize = TaxPayerBailoutDB.NUMBER_OF_RECORDS_DESIRED; + final int taxPayerListSize = dbSize / numberOfThreads; + System.out.println("Number of threads to run concurrently:" + numberOfThreads); + System.out.println("Tax payer database size:" + dbSize); + // + System.out.println("Createing tax payer database..."); + TaxPayerBailoutDB db = new TaxPayerBailoutDbImpl(dbSize); + List[] taxPayerList = new ArrayList[numberOfThreads]; + for (int i = 0; i < numberOfThreads; i++) { + taxPayerList[i] = new ArrayList(taxPayerListSize); + } + populateDatabase(db, taxPayerList, dbSize); + System.out.println("\tTax payer database created."); + System.out.println("Allocating(" + numberOfThreads + ")threads..."); + ExecutorService pool = Executors.newFixedThreadPool(numberOfThreads); + Callable[] callables = new TaxCallable[numberOfThreads]; + for (int i = 0; i < callables.length; i++) { + callables[i] = new TaxCallable(taxPayerList[i], db); + } + System.out.println("\tthreads allocated."); + + System.out.println("Starting(" + callables.length + ") threads..."); + Set> set = new HashSet>(); + + for (int i = 0; i < callables.length; i++) { + Callable callable = callables[i]; + Future future = pool.submit(callable); + set.add(future); + } + System.out.println("\t(" + callables.length + ")threads started."); + + System.out.println("Waiting for " + TEST_TIME / 1000 + "seconds for(" + callables.length + ")threads to complete..."); + + double iterationsPerSecond = 0; + long recordsAdded = 0, recordsRemoved = 0; + long nullCounter = 0; + int counter = 1; + for (Future future : set) { + BailoutFuture result = null; + try { + result = future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + System.out.println("Iterations per second on thread[" + counter++ + "]->" + result.getInterationsPerSecond()); + iterationsPerSecond += result.getInterationsPerSecond(); + recordsAdded += result.getRecordsAdded(); + nullCounter = result.getNullCounter(); + } + DecimalFormat df = new DecimalFormat("#.##"); + System.out.println("Total iterations per second ---------->" + df.format(iterationsPerSecond)); + NumberFormat nf = NumberFormat.getInstance(); + System.out.println("Total records added ------------------>" + nf.format(recordsAdded)); + System.out.println("Total records removed ---------------->" + nf.format(recordsRemoved)); + System.out.println("Total records in db ------------------>" + nf.format(db.size())); + System.out.println("Total null records encountered: ------>" + nf.format(nullCounter)); + System.exit(0); + } + + private static void populateDatabase(TaxPayerBailoutDB db, List[] taxPayerList, int dbSize) { + for (int i = 0; i < dbSize; i++) { + String key = getRandomTaxPayerId(); + TaxPayerRecord tpr = makeTaxPayerRecord(); + db.add(key, tpr); + int index = i % taxPayerList.length; + taxPayerList[index].add(key); + } + } + + public static String getRandomTaxPayerId() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + sb.append(alphabet[index]); + } + return sb.toString(); + } + + public static TaxPayerRecord makeTaxPayerRecord() { + String firstName = getRandomName(); + String laseName = getRandomName(); + String ssn = getRandomSSN(); + String address = getRandomAddress(); + String city = getRandomCity(); + String state = getRandomState(); + return new TaxPayerRecord(firstName, laseName, ssn, address, city, state); + } + + private static String getRandomCity() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(5) + 6; + for (int i = 0; i < size; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomState() { + int index = random.nextInt(states.length); + return states[index]; + } + + private static String getRandomAddress() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(14) + 10; + for (int i = 0; i < size; i++) { + if (i < 5) { + int x = random.nextInt(8); + sb.append(x + 1); + } + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 5) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomSSN() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 11; i++) { + sb.append('-'); + } + int x = random.nextInt(9); + sb.append(x); + return sb.toString(); + } + + private static String getRandomName() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(8) + 5; + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fjj/pa/cas/TaxCallable.java b/src/main/java/com/fjj/pa/cas/TaxCallable.java new file mode 100644 index 0000000..5ca1784 --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/TaxCallable.java @@ -0,0 +1,111 @@ +package com.fjj.pa.cas; + + +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:59 + */ +public class TaxCallable implements Callable { + + private static long runTimeInMillis = BailoutMain.TEST_TIME; + final private static Random generator = BailoutMain.random; + private long nullCounter, recodesRemoved, newRecordsAdded; + private int index; + private String taxPayerId; + final private List taxPayerList; + final private TaxPayerBailoutDB db; + + public TaxCallable(List taxPayerList, TaxPayerBailoutDB db) { + this.taxPayerList = taxPayerList; + this.db = db; + index = 0; + } + + @Override + public BailoutFuture call() throws Exception { + long iterations = 0L, elapsedTime = 0L; + long startTime = System.currentTimeMillis(); + double iterationsPersecond = 0; + do { + setTaxPayer(); + iterations++; + TaxPayerRecord tpr = null; + if (iterations == Long.MAX_VALUE) { + long elapsed = System.currentTimeMillis() - startTime; + iterationsPersecond = iterations / ((double) (elapsed / 1000)); + System.err.println("Iteration counter about to overflow ..."); + System.err.println("Calculating counter about to second ..."); + System.err.println("Iterations per second: " + iterationsPersecond); + iterations = 0L; + startTime = System.currentTimeMillis(); + runTimeInMillis -= elapsed; + } + if (iterations % 1001 == 0) { + tpr = addNewTaxPayer(tpr); + } else if (iterations % 60195 == 0) { + tpr = removeTaxPayer(tpr); + } else { + tpr = updateTaxPayer(iterations, tpr); + } + if (iterations % 1000 == 0) { + elapsedTime = System.currentTimeMillis() - startTime; + } + } while (elapsedTime < runTimeInMillis); + if (iterations >= 1000) { + iterationsPersecond = iterations / ((double) (elapsedTime / 1000)); + } + BailoutFuture bailoutFuture = new BailoutFuture(iterationsPersecond, newRecordsAdded, recodesRemoved, nullCounter); + return bailoutFuture; + } + + private void setTaxPayer() { + if (++index >= taxPayerList.size()) { + index = 0; + } + this.taxPayerId = taxPayerList.get(index); + } + + private TaxPayerRecord removeTaxPayer(TaxPayerRecord tpr) { + tpr = db.remove(taxPayerId); + if (tpr != null) { + taxPayerList.remove(index); + recodesRemoved++; + } + return tpr; + } + + private TaxPayerRecord addNewTaxPayer(TaxPayerRecord tpr) { + String tmpTaxPayerId = BailoutMain.getRandomTaxPayerId(); + tpr = BailoutMain.makeTaxPayerRecord(); + TaxPayerRecord old = db.add(tmpTaxPayerId, tpr); + if (old == null) { + taxPayerList.add(tmpTaxPayerId); + newRecordsAdded++; + } + return tpr; + } + + + private TaxPayerRecord updateTaxPayer(long iteratios, TaxPayerRecord tpr) { + if (iteratios % 1001 == 0) { + tpr = db.get(taxPayerId); + } else { + tpr = db.get(taxPayerId); + if (tpr != null) { + long tax = generator.nextInt(10) + 15; + tpr.taxPaid(tax); + } + } + if (tpr == null) { + nullCounter++; + } + return tpr; + } + +} diff --git a/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDB.java b/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDB.java new file mode 100644 index 0000000..5e7fdb2 --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDB.java @@ -0,0 +1,17 @@ +package com.fjj.pa.cas; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:35 + */ +public interface TaxPayerBailoutDB { + + static final int NUMBER_OF_RECORDS_DESIRED = 2*1000000; + + TaxPayerRecord get(String id); + TaxPayerRecord add(String id, TaxPayerRecord record); + TaxPayerRecord remove(String id); + int size(); +} diff --git a/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDbImpl.java b/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDbImpl.java new file mode 100644 index 0000000..2e7c4bc --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/TaxPayerBailoutDbImpl.java @@ -0,0 +1,46 @@ +package com.fjj.pa.cas; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:37 + */ +public class TaxPayerBailoutDbImpl implements TaxPayerBailoutDB { + + private final Map db; + + public TaxPayerBailoutDbImpl(int size){ +// db = Collections.synchronizedMap(new HashMap(size)); + db = new ConcurrentHashMap(size); + } + + @Override + public TaxPayerRecord get(String id) { + return db.get(id); + } + + @Override + public TaxPayerRecord add(String id, TaxPayerRecord record) { + TaxPayerRecord old = db.put(id,record); + if(old!=null){ + old = db.put(id,old); + } + return old; + } + + @Override + public TaxPayerRecord remove(String id) { + return db.remove(id); + } + + @Override + public int size() { + return db.size(); + } +} diff --git a/src/main/java/com/fjj/pa/cas/TaxPayerRecord.java b/src/main/java/com/fjj/pa/cas/TaxPayerRecord.java new file mode 100644 index 0000000..85dd183 --- /dev/null +++ b/src/main/java/com/fjj/pa/cas/TaxPayerRecord.java @@ -0,0 +1,36 @@ +package com.fjj.pa.cas; + +import lombok.Data; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:31 + */ +@Data +public class TaxPayerRecord { + + private String firstName,lastName,ssn,address,city,state; + private AtomicLong taxPaid; + + public TaxPayerRecord(String firstName,String lastName,String ssn,String address,String city,String state){ + this.firstName = firstName; + this.lastName = lastName; + this.ssn = ssn; + this.address = address; + this.city = city; + this.state = state; + this.taxPaid = new AtomicLong(0); + } + + public void taxPaid(long amout){ + taxPaid.addAndGet(amout); + } + + public long getTaxPaid(){ + return taxPaid.get(); + } +} diff --git a/src/main/java/com/fjj/pa/resize/BailoutFuture.java b/src/main/java/com/fjj/pa/resize/BailoutFuture.java new file mode 100644 index 0000000..5beae04 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/BailoutFuture.java @@ -0,0 +1,18 @@ +package com.fjj.pa.resize; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 14:58 + */ +@Data +@AllArgsConstructor +public class BailoutFuture { + + private double interationsPerSecond; + private long recordsAdded, recordsRemoced, nullCounter; +} diff --git a/src/main/java/com/fjj/pa/resize/BailoutMain.java b/src/main/java/com/fjj/pa/resize/BailoutMain.java new file mode 100644 index 0000000..6bc5131 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/BailoutMain.java @@ -0,0 +1,182 @@ +package com.fjj.pa.resize; + + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.*; + +public class BailoutMain { + final public static int TEST_TIME = 120 * 1000; + final public static Random random = new Random(Thread.currentThread().getId()); + private static char[] alphabet = {'a', 'b', 'c', 'd', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + public static String[] states = {"Alabama", "Alaska", "Arizona", + "Arkansas", "California", "Colorado", "Connecticut", + "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", + "Illinois", "Indiana", "Iowa", + "Kansas", "Kentucky", + "Louisiana", "Maine", "Maryland", "Massachusetts", + "Michigan", "Minnesota", "Mississippi, Missouri", "Montana", "Nebraska", + "Nevada ", "New Hampshire", "New Jersey, New Mexiew York", + "North Carolina", "North Dakota", "Ohioklahoma0regon", "Pennsylvania", + "Rhode Island", + "South Carolina", "South Dakota", "Tennessee", "Texas", + "Utah Vermont", "Virginia", "Washington", + "West Virgina", + "Wisconsin", "Wyoming"}; + + public static void main(String[] args) { + final int numberOfThreads = Runtime.getRuntime().availableProcessors(); +// final int numberOfThreads = 4; + final int dbSize = TaxPayerBailoutDB.NUMBER_OF_RECORDS_DESIRED; + final int taxPayerListSize = dbSize / numberOfThreads; + System.out.println("Number of threads to run concurrently:" + numberOfThreads); + System.out.println("Tax payer database size:" + dbSize); + // + System.out.println("Createing tax payer database..."); + TaxPayerBailoutDB db = new TaxPayerBailoutDbImpl(dbSize,states.length); + List[] taxPayerList = new ArrayList[numberOfThreads]; + for (int i = 0; i < numberOfThreads; i++) { + taxPayerList[i] = new ArrayList(taxPayerListSize); + } + populateDatabase(db, taxPayerList, dbSize); + System.out.println("\tTax payer database created."); + System.out.println("Allocating(" + numberOfThreads + ")threads..."); + ExecutorService pool = Executors.newFixedThreadPool(numberOfThreads); + Callable[] callables = new TaxCallable[numberOfThreads]; + for (int i = 0; i < callables.length; i++) { + callables[i] = new TaxCallable(taxPayerList[i], db); + } + System.out.println("\tthreads allocated."); + + System.out.println("Starting(" + callables.length + ") threads..."); + Set> set = new HashSet>(); + + for (int i = 0; i < callables.length; i++) { + Callable callable = callables[i]; + Future future = pool.submit(callable); + set.add(future); + } + System.out.println("\t(" + callables.length + ")threads started."); + + System.out.println("Waiting for " + TEST_TIME / 1000 + "seconds for(" + callables.length + ")threads to complete..."); + + double iterationsPerSecond = 0; + long recordsAdded = 0, recordsRemoved = 0; + long nullCounter = 0; + int counter = 1; + for (Future future : set) { + BailoutFuture result = null; + try { + result = future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + System.out.println("Iterations per second on thread[" + counter++ + "]->" + result.getInterationsPerSecond()); + iterationsPerSecond += result.getInterationsPerSecond(); + recordsAdded += result.getRecordsAdded(); + nullCounter = result.getNullCounter(); + } + DecimalFormat df = new DecimalFormat("#.##"); + System.out.println("Total iterations per second ---------->" + df.format(iterationsPerSecond)); + NumberFormat nf = NumberFormat.getInstance(); + System.out.println("Total records added ------------------>" + nf.format(recordsAdded)); + System.out.println("Total records removed ---------------->" + nf.format(recordsRemoved)); + System.out.println("Total records in db ------------------>" + nf.format(db.size())); + System.out.println("Total null records encountered: ------>" + nf.format(nullCounter)); + System.exit(0); + } + + private static void populateDatabase(TaxPayerBailoutDB db, List[] taxPayerList, int dbSize) { + for (int i = 0; i < dbSize; i++) { + String key = getRandomTaxPayerId(); + TaxPayerRecord tpr = makeTaxPayerRecord(); + db.add(key, tpr); + int index = i % taxPayerList.length; + taxPayerList[index].add(new StateAndId(key,tpr.getState())); + } + } + + public static String getRandomTaxPayerId() { + StringBuilder sb = new StringBuilder(20); + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + sb.append(alphabet[index]); + } + return sb.toString(); + } + + public static TaxPayerRecord makeTaxPayerRecord() { + String firstName = getRandomName(); + String laseName = getRandomName(); + String ssn = getRandomSSN(); + String address = getRandomAddress(); + String city = getRandomCity(); + String state = getRandomState(); + return new TaxPayerRecord(firstName, laseName, ssn, address, city, state); + } + + private static String getRandomCity() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(5) + 6; + for (int i = 0; i < size; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomState() { + int index = random.nextInt(states.length); + return states[index]; + } + + private static String getRandomAddress() { + StringBuilder sb = new StringBuilder(24); + int size = random.nextInt(14) + 10; + for (int i = 0; i < size; i++) { + if (i < 5) { + int x = random.nextInt(8); + sb.append(x + 1); + } + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 5) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomSSN() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 11; i++) { + sb.append('-'); + } + int x = random.nextInt(9); + sb.append(x); + return sb.toString(); + } + + private static String getRandomName() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(8) + 5; + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fjj/pa/resize/StateAndId.java b/src/main/java/com/fjj/pa/resize/StateAndId.java new file mode 100644 index 0000000..f4f15de --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/StateAndId.java @@ -0,0 +1,16 @@ +package com.fjj.pa.resize; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/23 10:42 + */ +@Data +@AllArgsConstructor +public class StateAndId { + String id,state; +} diff --git a/src/main/java/com/fjj/pa/resize/TaxCallable.java b/src/main/java/com/fjj/pa/resize/TaxCallable.java new file mode 100644 index 0000000..ea55887 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/TaxCallable.java @@ -0,0 +1,118 @@ +package com.fjj.pa.resize; + + +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:59 + */ +public class TaxCallable implements Callable { + + private static long runTimeInMillis = BailoutMain.TEST_TIME; + final private static ThreadLocal threadLocalRandom = new ThreadLocal() { + @Override + protected Random initialValue() { + return new Random(Thread.currentThread().getId()); + } + }; + private long nullCounter, recodesRemoved, newRecordsAdded; + private int index; + private String taxPayerId; + private StateAndId stateAndId; + final private List taxPayerList; + final private TaxPayerBailoutDB db; + + public TaxCallable(List taxPayerList, TaxPayerBailoutDB db) { + this.taxPayerList = taxPayerList; + this.db = db; + index = 0; + } + + @Override + public BailoutFuture call() throws Exception { + long iterations = 0L, elapsedTime = 0L; + long startTime = System.currentTimeMillis(); + double iterationsPersecond = 0; + do { + setTaxPayer(); + iterations++; + TaxPayerRecord tpr = null; + if (iterations == Long.MAX_VALUE) { + long elapsed = System.currentTimeMillis() - startTime; + iterationsPersecond = iterations / ((double) (elapsed / 1000)); + System.err.println("Iteration counter about to overflow ..."); + System.err.println("Calculating counter about to second ..."); + System.err.println("Iterations per second: " + iterationsPersecond); + iterations = 0L; + startTime = System.currentTimeMillis(); + runTimeInMillis -= elapsed; + } + if (iterations % 1001 == 0) { + tpr = addNewTaxPayer(tpr); + } else if (iterations % 60195 == 0) { + tpr = removeTaxPayer(tpr); + } else { + tpr = updateTaxPayer(iterations, tpr); + } + if (iterations % 1000 == 0) { + elapsedTime = System.currentTimeMillis() - startTime; + } + } while (elapsedTime < runTimeInMillis); + if (iterations >= 1000) { + iterationsPersecond = iterations / ((double) (elapsedTime / 1000)); + } + BailoutFuture bailoutFuture = new BailoutFuture(iterationsPersecond, newRecordsAdded, recodesRemoved, nullCounter); + return bailoutFuture; + } + + private void setTaxPayer() { + if (++index >= taxPayerList.size()) { + index = 0; + } + this.stateAndId = taxPayerList.get(index); + } + + private TaxPayerRecord removeTaxPayer(TaxPayerRecord tpr) { + tpr = db.remove(stateAndId.getId(),stateAndId.getState()); + if (tpr != null) { + taxPayerList.remove(index); + recodesRemoved++; + } + return tpr; + } + + private TaxPayerRecord addNewTaxPayer(TaxPayerRecord tpr) { + String tmpTaxPayerId = BailoutMain.getRandomTaxPayerId(); + tpr = BailoutMain.makeTaxPayerRecord(); + TaxPayerRecord old = db.add(tmpTaxPayerId, tpr); + if (old == null) { + StateAndId sai = new StateAndId(tmpTaxPayerId,tpr.getState()); + taxPayerList.add(sai); + newRecordsAdded++; + } + return tpr; + } + + + private TaxPayerRecord updateTaxPayer(long iteratios, TaxPayerRecord tpr) { + if (iteratios % 1001 == 0) { + tpr = db.get(stateAndId.getId(),stateAndId.getState()); + } else { + tpr = db.get(stateAndId.getId(),stateAndId.getState()); + if (tpr != null) { + long tax = threadLocalRandom.get().nextInt(10) + 15; + tpr.taxPaid(tax); + } + } + if (tpr == null) { + nullCounter++; + } + return tpr; + } + +} diff --git a/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDB.java b/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDB.java new file mode 100644 index 0000000..88e36b2 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDB.java @@ -0,0 +1,17 @@ +package com.fjj.pa.resize; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:35 + */ +public interface TaxPayerBailoutDB { + + static final int NUMBER_OF_RECORDS_DESIRED = 2*1000000; + + TaxPayerRecord get(String id, String states); + TaxPayerRecord add(String id, TaxPayerRecord record); + TaxPayerRecord remove(String id, String states); + int size(); +} diff --git a/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDbImpl.java b/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDbImpl.java new file mode 100644 index 0000000..f60d9b0 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/TaxPayerBailoutDbImpl.java @@ -0,0 +1,68 @@ +package com.fjj.pa.resize; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:37 + */ +public class TaxPayerBailoutDbImpl implements TaxPayerBailoutDB { + + // private final Map db; + private final Map> db; + + public TaxPayerBailoutDbImpl(int size, int states) { + db = new HashMap>(); + for (int i = 0; i < states; i++) { +// Map map = Collections.synchronizedMap(new HashMap(NUMBER_OF_RECORDS_DESIRED / states)); + Map map = new ConcurrentHashMap(NUMBER_OF_RECORDS_DESIRED / states); + db.put(BailoutMain.states[i], map); + } + } + + @Override + public TaxPayerRecord get(String id, String states) { + Map record = getStateMap(states); + if (record == null) { + System.out.println("Unable find state:" + states); + } + return record.get(id); + } + + @Override + public TaxPayerRecord add(String id, TaxPayerRecord record) { + Map stateMap = getStateMap(record.getState()); + TaxPayerRecord old = stateMap.put(id, record); + if (old != null) { + old = stateMap.put(id, old); + } + return old; + } + + @Override + public TaxPayerRecord remove(String id, String states) { + Map stateMap = getStateMap(states); + TaxPayerRecord tpr = null; + if (stateMap != null) { + tpr = stateMap.remove(id); + } + return tpr; + } + + @Override + public int size() { + return db.size(); + } + + private Map getStateMap(String states) { + Map stringTaxPayerRecordMap = db.get(states); + if (stringTaxPayerRecordMap == null) { + throw new UnsupportedOperationException("State:" + states + " not found."); + } + return stringTaxPayerRecordMap; + } +} diff --git a/src/main/java/com/fjj/pa/resize/TaxPayerRecord.java b/src/main/java/com/fjj/pa/resize/TaxPayerRecord.java new file mode 100644 index 0000000..18765f9 --- /dev/null +++ b/src/main/java/com/fjj/pa/resize/TaxPayerRecord.java @@ -0,0 +1,36 @@ +package com.fjj.pa.resize; + +import lombok.Data; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:31 + */ +@Data +public class TaxPayerRecord { + + private String firstName,lastName,ssn,address,city,state; + private AtomicLong taxPaid; + + public TaxPayerRecord(String firstName,String lastName,String ssn,String address,String city,String state){ + this.firstName = firstName; + this.lastName = lastName; + this.ssn = ssn; + this.address = address; + this.city = city; + this.state = state; + this.taxPaid = new AtomicLong(0); + } + + public void taxPaid(long amout){ + taxPaid.addAndGet(amout); + } + + public long getTaxPaid(){ + return taxPaid.get(); + } +} diff --git a/src/main/java/com/fjj/pa/segment/BailoutFuture.java b/src/main/java/com/fjj/pa/segment/BailoutFuture.java new file mode 100644 index 0000000..b4a66d4 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/BailoutFuture.java @@ -0,0 +1,18 @@ +package com.fjj.pa.segment; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 14:58 + */ +@Data +@AllArgsConstructor +public class BailoutFuture { + + private double interationsPerSecond; + private long recordsAdded, recordsRemoced, nullCounter; +} diff --git a/src/main/java/com/fjj/pa/segment/BailoutMain.java b/src/main/java/com/fjj/pa/segment/BailoutMain.java new file mode 100644 index 0000000..4027073 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/BailoutMain.java @@ -0,0 +1,182 @@ +package com.fjj.pa.segment; + + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.*; + +public class BailoutMain { + final public static int TEST_TIME = 120 * 1000; + final public static Random random = new Random(Thread.currentThread().getId()); + private static char[] alphabet = {'a', 'b', 'c', 'd', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + public static String[] states = {"Alabama", "Alaska", "Arizona", + "Arkansas", "California", "Colorado", "Connecticut", + "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", + "Illinois", "Indiana", "Iowa", + "Kansas", "Kentucky", + "Louisiana", "Maine", "Maryland", "Massachusetts", + "Michigan", "Minnesota", "Mississippi, Missouri", "Montana", "Nebraska", + "Nevada ", "New Hampshire", "New Jersey, New Mexiew York", + "North Carolina", "North Dakota", "Ohioklahoma0regon", "Pennsylvania", + "Rhode Island", + "South Carolina", "South Dakota", "Tennessee", "Texas", + "Utah Vermont", "Virginia", "Washington", + "West Virgina", + "Wisconsin", "Wyoming"}; + + public static void main(String[] args) { + final int numberOfThreads = Runtime.getRuntime().availableProcessors(); +// final int numberOfThreads = 4; + final int dbSize = TaxPayerBailoutDB.NUMBER_OF_RECORDS_DESIRED; + final int taxPayerListSize = dbSize / numberOfThreads; + System.out.println("Number of threads to run concurrently:" + numberOfThreads); + System.out.println("Tax payer database size:" + dbSize); + // + System.out.println("Createing tax payer database..."); + TaxPayerBailoutDB db = new TaxPayerBailoutDbImpl(dbSize,states.length); + List[] taxPayerList = new ArrayList[numberOfThreads]; + for (int i = 0; i < numberOfThreads; i++) { + taxPayerList[i] = new ArrayList(taxPayerListSize); + } + populateDatabase(db, taxPayerList, dbSize); + System.out.println("\tTax payer database created."); + System.out.println("Allocating(" + numberOfThreads + ")threads..."); + ExecutorService pool = Executors.newFixedThreadPool(numberOfThreads); + Callable[] callables = new TaxCallable[numberOfThreads]; + for (int i = 0; i < callables.length; i++) { + callables[i] = new TaxCallable(taxPayerList[i], db); + } + System.out.println("\tthreads allocated."); + + System.out.println("Starting(" + callables.length + ") threads..."); + Set> set = new HashSet>(); + + for (int i = 0; i < callables.length; i++) { + Callable callable = callables[i]; + Future future = pool.submit(callable); + set.add(future); + } + System.out.println("\t(" + callables.length + ")threads started."); + + System.out.println("Waiting for " + TEST_TIME / 1000 + "seconds for(" + callables.length + ")threads to complete..."); + + double iterationsPerSecond = 0; + long recordsAdded = 0, recordsRemoved = 0; + long nullCounter = 0; + int counter = 1; + for (Future future : set) { + BailoutFuture result = null; + try { + result = future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + System.out.println("Iterations per second on thread[" + counter++ + "]->" + result.getInterationsPerSecond()); + iterationsPerSecond += result.getInterationsPerSecond(); + recordsAdded += result.getRecordsAdded(); + nullCounter = result.getNullCounter(); + } + DecimalFormat df = new DecimalFormat("#.##"); + System.out.println("Total iterations per second ---------->" + df.format(iterationsPerSecond)); + NumberFormat nf = NumberFormat.getInstance(); + System.out.println("Total records added ------------------>" + nf.format(recordsAdded)); + System.out.println("Total records removed ---------------->" + nf.format(recordsRemoved)); + System.out.println("Total records in db ------------------>" + nf.format(db.size())); + System.out.println("Total null records encountered: ------>" + nf.format(nullCounter)); + System.exit(0); + } + + private static void populateDatabase(TaxPayerBailoutDB db, List[] taxPayerList, int dbSize) { + for (int i = 0; i < dbSize; i++) { + String key = getRandomTaxPayerId(); + TaxPayerRecord tpr = makeTaxPayerRecord(); + db.add(key, tpr); + int index = i % taxPayerList.length; + taxPayerList[index].add(new StateAndId(key,tpr.getState())); + } + } + + public static String getRandomTaxPayerId() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + sb.append(alphabet[index]); + } + return sb.toString(); + } + + public static TaxPayerRecord makeTaxPayerRecord() { + String firstName = getRandomName(); + String laseName = getRandomName(); + String ssn = getRandomSSN(); + String address = getRandomAddress(); + String city = getRandomCity(); + String state = getRandomState(); + return new TaxPayerRecord(firstName, laseName, ssn, address, city, state); + } + + private static String getRandomCity() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(5) + 6; + for (int i = 0; i < size; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomState() { + int index = random.nextInt(states.length); + return states[index]; + } + + private static String getRandomAddress() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(14) + 10; + for (int i = 0; i < size; i++) { + if (i < 5) { + int x = random.nextInt(8); + sb.append(x + 1); + } + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 5) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomSSN() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 11; i++) { + sb.append('-'); + } + int x = random.nextInt(9); + sb.append(x); + return sb.toString(); + } + + private static String getRandomName() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(8) + 5; + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fjj/pa/segment/StateAndId.java b/src/main/java/com/fjj/pa/segment/StateAndId.java new file mode 100644 index 0000000..07cdf61 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/StateAndId.java @@ -0,0 +1,16 @@ +package com.fjj.pa.segment; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/23 10:42 + */ +@Data +@AllArgsConstructor +public class StateAndId { + String id,state; +} diff --git a/src/main/java/com/fjj/pa/segment/TaxCallable.java b/src/main/java/com/fjj/pa/segment/TaxCallable.java new file mode 100644 index 0000000..bd166ae --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/TaxCallable.java @@ -0,0 +1,119 @@ +package com.fjj.pa.segment; + + +import javax.swing.plaf.nimbus.State; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:59 + */ +public class TaxCallable implements Callable { + + private static long runTimeInMillis = BailoutMain.TEST_TIME; + final private static ThreadLocal threadLocalRandom = new ThreadLocal() { + @Override + protected Random initialValue() { + return new Random(Thread.currentThread().getId()); + } + }; + private long nullCounter, recodesRemoved, newRecordsAdded; + private int index; + private String taxPayerId; + private StateAndId stateAndId; + final private List taxPayerList; + final private TaxPayerBailoutDB db; + + public TaxCallable(List taxPayerList, TaxPayerBailoutDB db) { + this.taxPayerList = taxPayerList; + this.db = db; + index = 0; + } + + @Override + public BailoutFuture call() throws Exception { + long iterations = 0L, elapsedTime = 0L; + long startTime = System.currentTimeMillis(); + double iterationsPersecond = 0; + do { + setTaxPayer(); + iterations++; + TaxPayerRecord tpr = null; + if (iterations == Long.MAX_VALUE) { + long elapsed = System.currentTimeMillis() - startTime; + iterationsPersecond = iterations / ((double) (elapsed / 1000)); + System.err.println("Iteration counter about to overflow ..."); + System.err.println("Calculating counter about to second ..."); + System.err.println("Iterations per second: " + iterationsPersecond); + iterations = 0L; + startTime = System.currentTimeMillis(); + runTimeInMillis -= elapsed; + } + if (iterations % 1001 == 0) { + tpr = addNewTaxPayer(tpr); + } else if (iterations % 60195 == 0) { + tpr = removeTaxPayer(tpr); + } else { + tpr = updateTaxPayer(iterations, tpr); + } + if (iterations % 1000 == 0) { + elapsedTime = System.currentTimeMillis() - startTime; + } + } while (elapsedTime < runTimeInMillis); + if (iterations >= 1000) { + iterationsPersecond = iterations / ((double) (elapsedTime / 1000)); + } + BailoutFuture bailoutFuture = new BailoutFuture(iterationsPersecond, newRecordsAdded, recodesRemoved, nullCounter); + return bailoutFuture; + } + + private void setTaxPayer() { + if (++index >= taxPayerList.size()) { + index = 0; + } + this.stateAndId = taxPayerList.get(index); + } + + private TaxPayerRecord removeTaxPayer(TaxPayerRecord tpr) { + tpr = db.remove(stateAndId.getId(),stateAndId.getState()); + if (tpr != null) { + taxPayerList.remove(index); + recodesRemoved++; + } + return tpr; + } + + private TaxPayerRecord addNewTaxPayer(TaxPayerRecord tpr) { + String tmpTaxPayerId = BailoutMain.getRandomTaxPayerId(); + tpr = BailoutMain.makeTaxPayerRecord(); + TaxPayerRecord old = db.add(tmpTaxPayerId, tpr); + if (old == null) { + StateAndId sai = new StateAndId(tmpTaxPayerId,tpr.getState()); + taxPayerList.add(sai); + newRecordsAdded++; + } + return tpr; + } + + + private TaxPayerRecord updateTaxPayer(long iteratios, TaxPayerRecord tpr) { + if (iteratios % 1001 == 0) { + tpr = db.get(stateAndId.getId(),stateAndId.getState()); + } else { + tpr = db.get(stateAndId.getId(),stateAndId.getState()); + if (tpr != null) { + long tax = threadLocalRandom.get().nextInt(10) + 15; + tpr.taxPaid(tax); + } + } + if (tpr == null) { + nullCounter++; + } + return tpr; + } + +} diff --git a/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDB.java b/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDB.java new file mode 100644 index 0000000..a89f757 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDB.java @@ -0,0 +1,17 @@ +package com.fjj.pa.segment; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:35 + */ +public interface TaxPayerBailoutDB { + + static final int NUMBER_OF_RECORDS_DESIRED = 2*1000000; + + TaxPayerRecord get(String id,String states); + TaxPayerRecord add(String id, TaxPayerRecord record); + TaxPayerRecord remove(String id,String states); + int size(); +} diff --git a/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDbImpl.java b/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDbImpl.java new file mode 100644 index 0000000..0883d75 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/TaxPayerBailoutDbImpl.java @@ -0,0 +1,69 @@ +package com.fjj.pa.segment; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:37 + */ +public class TaxPayerBailoutDbImpl implements TaxPayerBailoutDB { + + // private final Map db; + private final Map> db; + + public TaxPayerBailoutDbImpl(int size, int states) { + db = new HashMap>(); + for (int i = 0; i < states; i++) { +// Map map = Collections.synchronizedMap(new HashMap(NUMBER_OF_RECORDS_DESIRED / states)); + Map map = new ConcurrentHashMap(NUMBER_OF_RECORDS_DESIRED / states); + db.put(BailoutMain.states[i], map); + } + } + + @Override + public TaxPayerRecord get(String id, String states) { + Map record = getStateMap(states); + if (record == null) { + System.out.println("Unable find state:" + states); + } + return record.get(id); + } + + @Override + public TaxPayerRecord add(String id, TaxPayerRecord record) { + Map stateMap = getStateMap(record.getState()); + TaxPayerRecord old = stateMap.put(id, record); + if (old != null) { + old = stateMap.put(id, old); + } + return old; + } + + @Override + public TaxPayerRecord remove(String id, String states) { + Map stateMap = getStateMap(states); + TaxPayerRecord tpr = null; + if (stateMap != null) { + tpr = stateMap.remove(id); + } + return tpr; + } + + @Override + public int size() { + return db.size(); + } + + private Map getStateMap(String states) { + Map stringTaxPayerRecordMap = db.get(states); + if (stringTaxPayerRecordMap == null) { + throw new UnsupportedOperationException("State:" + states + " not found."); + } + return stringTaxPayerRecordMap; + } +} diff --git a/src/main/java/com/fjj/pa/segment/TaxPayerRecord.java b/src/main/java/com/fjj/pa/segment/TaxPayerRecord.java new file mode 100644 index 0000000..dd79937 --- /dev/null +++ b/src/main/java/com/fjj/pa/segment/TaxPayerRecord.java @@ -0,0 +1,36 @@ +package com.fjj.pa.segment; + +import lombok.Data; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:31 + */ +@Data +public class TaxPayerRecord { + + private String firstName,lastName,ssn,address,city,state; + private AtomicLong taxPaid; + + public TaxPayerRecord(String firstName,String lastName,String ssn,String address,String city,String state){ + this.firstName = firstName; + this.lastName = lastName; + this.ssn = ssn; + this.address = address; + this.city = city; + this.state = state; + this.taxPaid = new AtomicLong(0); + } + + public void taxPaid(long amout){ + taxPaid.addAndGet(amout); + } + + public long getTaxPaid(){ + return taxPaid.get(); + } +} diff --git a/src/main/java/com/fjj/pa/threadlocal/BailoutFuture.java b/src/main/java/com/fjj/pa/threadlocal/BailoutFuture.java new file mode 100644 index 0000000..9952f5f --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/BailoutFuture.java @@ -0,0 +1,18 @@ +package com.fjj.pa.threadlocal; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 14:58 + */ +@Data +@AllArgsConstructor +public class BailoutFuture { + + private double interationsPerSecond; + private long recordsAdded, recordsRemoced, nullCounter; +} diff --git a/src/main/java/com/fjj/pa/threadlocal/BailoutMain.java b/src/main/java/com/fjj/pa/threadlocal/BailoutMain.java new file mode 100644 index 0000000..ff33415 --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/BailoutMain.java @@ -0,0 +1,181 @@ +package com.fjj.pa.threadlocal; + + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.*; + +public class BailoutMain { + final public static int TEST_TIME = 120 * 1000; + final public static Random random = new Random(Thread.currentThread().getId()); + private static char[] alphabet = {'a', 'b', 'c', 'd', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + private static String[] states = {"Alabama", "Alaska", "Arizona", + "Arkansas", "California", "Colorado", "Connecticut", + "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", + "Illinois", "Indiana", "Iowa", + "Kansas", "Kentucky", + "Louisiana", "Maine", "Maryland", "Massachusetts", + "Michigan", "Minnesota", "Mississippi, Missouri", "Montana", "Nebraska", + "Nevada ", "New Hampshire", "New Jersey, New Mexiew York", + "North Carolina", "North Dakota", "Ohioklahoma0regon", "Pennsylvania", + "Rhode Island", + "South Carolina", "South Dakota", "Tennessee", "Texas", + "Utah Vermont", "Virginia", "Washington", + "West Virgina", + "Wisconsin", "Wyoming"}; + + public static void main(String[] args) { + final int numberOfThreads = Runtime.getRuntime().availableProcessors(); + final int dbSize = TaxPayerBailoutDB.NUMBER_OF_RECORDS_DESIRED; + final int taxPayerListSize = dbSize / numberOfThreads; + System.out.println("Number of threads to run concurrently:" + numberOfThreads); + System.out.println("Tax payer database size:" + dbSize); + // + System.out.println("Createing tax payer database..."); + TaxPayerBailoutDB db = new TaxPayerBailoutDbImpl(dbSize); + List[] taxPayerList = new ArrayList[numberOfThreads]; + for (int i = 0; i < numberOfThreads; i++) { + taxPayerList[i] = new ArrayList(taxPayerListSize); + } + populateDatabase(db, taxPayerList, dbSize); + System.out.println("\tTax payer database created."); + System.out.println("Allocating(" + numberOfThreads + ")threads..."); + ExecutorService pool = Executors.newFixedThreadPool(numberOfThreads); + Callable[] callables = new TaxCallable[numberOfThreads]; + for (int i = 0; i < callables.length; i++) { + callables[i] = new TaxCallable(taxPayerList[i], db); + } + System.out.println("\tthreads allocated."); + + System.out.println("Starting(" + callables.length + ") threads..."); + Set> set = new HashSet>(); + + for (int i = 0; i < callables.length; i++) { + Callable callable = callables[i]; + Future future = pool.submit(callable); + set.add(future); + } + System.out.println("\t(" + callables.length + ")threads started."); + + System.out.println("Waiting for " + TEST_TIME / 1000 + "seconds for(" + callables.length + ")threads to complete..."); + + double iterationsPerSecond = 0; + long recordsAdded = 0, recordsRemoved = 0; + long nullCounter = 0; + int counter = 1; + for (Future future : set) { + BailoutFuture result = null; + try { + result = future.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + System.out.println("Iterations per second on thread[" + counter++ + "]->" + result.getInterationsPerSecond()); + iterationsPerSecond += result.getInterationsPerSecond(); + recordsAdded += result.getRecordsAdded(); + nullCounter = result.getNullCounter(); + } + DecimalFormat df = new DecimalFormat("#.##"); + System.out.println("Total iterations per second ---------->" + df.format(iterationsPerSecond)); + NumberFormat nf = NumberFormat.getInstance(); + System.out.println("Total records added ------------------>" + nf.format(recordsAdded)); + System.out.println("Total records removed ---------------->" + nf.format(recordsRemoved)); + System.out.println("Total records in db ------------------>" + nf.format(db.size())); + System.out.println("Total null records encountered: ------>" + nf.format(nullCounter)); + System.exit(0); + } + + private static void populateDatabase(TaxPayerBailoutDB db, List[] taxPayerList, int dbSize) { + for (int i = 0; i < dbSize; i++) { + String key = getRandomTaxPayerId(); + TaxPayerRecord tpr = makeTaxPayerRecord(); + db.add(key, tpr); + int index = i % taxPayerList.length; + taxPayerList[index].add(key); + } + } + + public static String getRandomTaxPayerId() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + sb.append(alphabet[index]); + } + return sb.toString(); + } + + public static TaxPayerRecord makeTaxPayerRecord() { + String firstName = getRandomName(); + String laseName = getRandomName(); + String ssn = getRandomSSN(); + String address = getRandomAddress(); + String city = getRandomCity(); + String state = getRandomState(); + return new TaxPayerRecord(firstName, laseName, ssn, address, city, state); + } + + private static String getRandomCity() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(5) + 6; + for (int i = 0; i < size; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomState() { + int index = random.nextInt(states.length); + return states[index]; + } + + private static String getRandomAddress() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(14) + 10; + for (int i = 0; i < size; i++) { + if (i < 5) { + int x = random.nextInt(8); + sb.append(x + 1); + } + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 5) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + private static String getRandomSSN() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 11; i++) { + sb.append('-'); + } + int x = random.nextInt(9); + sb.append(x); + return sb.toString(); + } + + private static String getRandomName() { + StringBuilder sb = new StringBuilder(); + int size = random.nextInt(8) + 5; + for (int i = 0; i < 20; i++) { + int index = random.nextInt(alphabet.length); + char c = alphabet[index]; + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fjj/pa/threadlocal/TaxCallable.java b/src/main/java/com/fjj/pa/threadlocal/TaxCallable.java new file mode 100644 index 0000000..9fe24be --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/TaxCallable.java @@ -0,0 +1,117 @@ +package com.fjj.pa.threadlocal; + + +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:59 + */ +public class TaxCallable implements Callable { + + private static long runTimeInMillis = BailoutMain.TEST_TIME; + // final private static Random generator = BailoutMain.random; + final private static ThreadLocal threadLocalRandom = new ThreadLocal() { + @Override + protected Random initialValue() { + return new Random(Thread.currentThread().getId()); + } + }; + private long nullCounter, recodesRemoved, newRecordsAdded; + private int index; + private String taxPayerId; + final private List taxPayerList; + final private TaxPayerBailoutDB db; + + public TaxCallable(List taxPayerList, TaxPayerBailoutDB db) { + this.taxPayerList = taxPayerList; + this.db = db; + index = 0; + } + + @Override + public BailoutFuture call() throws Exception { + long iterations = 0L, elapsedTime = 0L; + long startTime = System.currentTimeMillis(); + double iterationsPersecond = 0; + do { + setTaxPayer(); + iterations++; + TaxPayerRecord tpr = null; + if (iterations == Long.MAX_VALUE) { + long elapsed = System.currentTimeMillis() - startTime; + iterationsPersecond = iterations / ((double) (elapsed / 1000)); + System.err.println("Iteration counter about to overflow ..."); + System.err.println("Calculating counter about to second ..."); + System.err.println("Iterations per second: " + iterationsPersecond); + iterations = 0L; + startTime = System.currentTimeMillis(); + runTimeInMillis -= elapsed; + } + if (iterations % 1001 == 0) { + tpr = addNewTaxPayer(tpr); + } else if (iterations % 60195 == 0) { + tpr = removeTaxPayer(tpr); + } else { + tpr = updateTaxPayer(iterations, tpr); + } + if (iterations % 1000 == 0) { + elapsedTime = System.currentTimeMillis() - startTime; + } + } while (elapsedTime < runTimeInMillis); + if (iterations >= 1000) { + iterationsPersecond = iterations / ((double) (elapsedTime / 1000)); + } + BailoutFuture bailoutFuture = new BailoutFuture(iterationsPersecond, newRecordsAdded, recodesRemoved, nullCounter); + return bailoutFuture; + } + + private void setTaxPayer() { + if (++index >= taxPayerList.size()) { + index = 0; + } + this.taxPayerId = taxPayerList.get(index); + } + + private TaxPayerRecord removeTaxPayer(TaxPayerRecord tpr) { + tpr = db.remove(taxPayerId); + if (tpr != null) { + taxPayerList.remove(index); + recodesRemoved++; + } + return tpr; + } + + private TaxPayerRecord addNewTaxPayer(TaxPayerRecord tpr) { + String tmpTaxPayerId = BailoutMain.getRandomTaxPayerId(); + tpr = BailoutMain.makeTaxPayerRecord(); + TaxPayerRecord old = db.add(tmpTaxPayerId, tpr); + if (old == null) { + taxPayerList.add(tmpTaxPayerId); + newRecordsAdded++; + } + return tpr; + } + + + private TaxPayerRecord updateTaxPayer(long iteratios, TaxPayerRecord tpr) { + if (iteratios % 1001 == 0) { + tpr = db.get(taxPayerId); + } else { + tpr = db.get(taxPayerId); + if (tpr != null) { + long tax = threadLocalRandom.get().nextInt(10) + 15; + tpr.taxPaid(tax); + } + } + if (tpr == null) { + nullCounter++; + } + return tpr; + } + +} diff --git a/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDB.java b/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDB.java new file mode 100644 index 0000000..0efa462 --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDB.java @@ -0,0 +1,17 @@ +package com.fjj.pa.threadlocal; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:35 + */ +public interface TaxPayerBailoutDB { + + static final int NUMBER_OF_RECORDS_DESIRED = 2*1000000; + + TaxPayerRecord get(String id); + TaxPayerRecord add(String id, TaxPayerRecord record); + TaxPayerRecord remove(String id); + int size(); +} diff --git a/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDbImpl.java b/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDbImpl.java new file mode 100644 index 0000000..9e8cfbf --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/TaxPayerBailoutDbImpl.java @@ -0,0 +1,46 @@ +package com.fjj.pa.threadlocal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:37 + */ +public class TaxPayerBailoutDbImpl implements TaxPayerBailoutDB { + + private final Map db; + + public TaxPayerBailoutDbImpl(int size){ +// db = Collections.synchronizedMap(new HashMap(size)); + db = new ConcurrentHashMap(size); + } + + @Override + public TaxPayerRecord get(String id) { + return db.get(id); + } + + @Override + public TaxPayerRecord add(String id, TaxPayerRecord record) { + TaxPayerRecord old = db.put(id,record); + if(old!=null){ + old = db.put(id,old); + } + return old; + } + + @Override + public TaxPayerRecord remove(String id) { + return db.remove(id); + } + + @Override + public int size() { + return db.size(); + } +} diff --git a/src/main/java/com/fjj/pa/threadlocal/TaxPayerRecord.java b/src/main/java/com/fjj/pa/threadlocal/TaxPayerRecord.java new file mode 100644 index 0000000..8a8e474 --- /dev/null +++ b/src/main/java/com/fjj/pa/threadlocal/TaxPayerRecord.java @@ -0,0 +1,36 @@ +package com.fjj.pa.threadlocal; + +import lombok.Data; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * ${description} + * + * @author Fjj + * @date 2020/10/21 16:31 + */ +@Data +public class TaxPayerRecord { + + private String firstName,lastName,ssn,address,city,state; + private AtomicLong taxPaid; + + public TaxPayerRecord(String firstName,String lastName,String ssn,String address,String city,String state){ + this.firstName = firstName; + this.lastName = lastName; + this.ssn = ssn; + this.address = address; + this.city = city; + this.state = state; + this.taxPaid = new AtomicLong(0); + } + + public void taxPaid(long amout){ + taxPaid.addAndGet(amout); + } + + public long getTaxPaid(){ + return taxPaid.get(); + } +} -- GitLab