package com.spring.test.th;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
/**
* Produce consumer mode 2.
* 多生產者、消費者、共享對象簡單協作。
* @author Spring
* @year 2020
*
*/
public class ProduceConsumer2 {
public static void main(String[] args) {
// Try a mode, with multiple shells, multiple producers, multiple consumers.
FnyShellMgr fsMgr = new FnyShellMgr();
// Create producer thread group. Do not use a scalable thread pool mode, only test.
LinkedList<Producer> lProcer = new LinkedList<Producer>();
Producer pd = new Producer(fsMgr);
lProcer.add(pd);
Producer pd2 = new Producer(fsMgr);
lProcer.add(pd2);
Producer pd3 = new Producer(fsMgr);
lProcer.add(pd3);
Producer pd4 = new Producer(fsMgr);
lProcer.add(pd4);
Producer pd5 = new Producer(fsMgr);
lProcer.add(pd5);
//Producer pd6 = new Producer(shell);
//Producer pd7 = new Producer(shell);
//Producer pd8 = new Producer(shell);
//Producer pd9 = new Producer(shell);
//---------------
// Create consumer thread group.
ArrayList<Consumer> aConsm = new ArrayList<Consumer>();
Consumer cs = new Consumer(fsMgr);
aConsm.add(cs);
Consumer cs2 = new Consumer(fsMgr);
aConsm.add(cs2);
Consumer cs3 = new Consumer(fsMgr);
aConsm.add(cs3);
//----------------
//System.out.println("=====================5-1- running!");
System.out.println("=====================Simple product cycle - start running!");
for (Producer pdr : lProcer) {
pdr.start();
}
for (Consumer csm : aConsm) {
csm.start();
}
int testTime = 700; // 1000 ms
try {
Thread.sleep(testTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====[Mode2] 上帝醒了,準備終止競賽!");
System.out.println("[Mode2] 開始中斷 csm");
for (Consumer csm : aConsm) {
while(csm.isAlive()) {
//csm.interrupt(); // may done fail.
csm.stop();
}
}
System.out.println("[Mode2] 開始中斷 pdc");
for (Producer pdr : lProcer) {
while(pdr.isAlive()) {
//pdr.interrupt(); // may done fail.
pdr.stop();
}
}
System.out.println("=====[Mode2] 競賽停止!");
}
}
/**
* Sync in object level.
*
*/
class MySyncShell {
private static final String simpleFmt = "YYYY-MM-dd hh:mm:ss.SSS";
//private final String uuid = "xxxxxxxx"; // when test, no used this.
private int fMAX_NUM = 10;
private int leftNum = 0;
private SimpleDateFormat dateFmt = new SimpleDateFormat(simpleFmt);
public MySyncShell(int maxLimit) {
this.fMAX_NUM = maxLimit;
}
synchronized void add(String idTag, Long tid) { // idTag is default set as class name.
//System.out.println("add, idTag is " + idTag);
if ("com.spring.test.th.Producer".equals(idTag)) {
// Goes down
} else {
return;
}
leftNum++;
System.out.println("after Producer [" + tid + "] add(), left " + leftNum
+ ", time " + dateFmt.format(Calendar.getInstance().getTime()));
}
synchronized void use(String idTag, Long tid) {
//System.out.println("use, idTag is " + idTag);
if ("com.spring.test.th.Consumer".equals(idTag)) {
// Goes down
} else {
return;
}
leftNum--;
System.out.println("after Consumer [" + tid + "] use(), left " + leftNum
+ ", time " + dateFmt.format(Calendar.getInstance().getTime()));
}
synchronized boolean isEmpty() {
if (leftNum < 0) {
throw new RuntimeException("Bad operation !!");
}
return leftNum == 0;
}
synchronized boolean isFull() {
if (leftNum >= fMAX_NUM) {
System.err.println("== [isFull] left num is over the max num = " + leftNum);
return true;
}
return false;
}
int getMaxCapacity() {
return fMAX_NUM;
}
}
class Producer extends Thread {
private static final String simpleFmt = "YYYY-MM-dd hh:mm:ss.sssss";
private final SimpleDateFormat dateFmt = new SimpleDateFormat(simpleFmt);
private final MySyncShell cmShell;
private final PrintStream ps = System.out;
public Producer(FnyShellMgr fsMgr) {
cmShell = fsMgr.requestAProductShell(this.getId());
}
@Override
public void run() {
super.run();
final long myId = getId();
ps.println("[Producer] run() tid = " + myId
+ ", date = " + dateFmt.format(Calendar.getInstance().getTime()));
ps.println(this.getId() + ", cmShell is " + cmShell);
if (cmShell == null) {
System.err.println("Error, cm shell is null !!");
return;
}
synchronized (cmShell) {
while(true) {
ps.println("[Producer " + myId + "] once done begin.");
while (cmShell.isFull()) {
//ps.println("[Producer] when sensed full, begin to notify.");
//cmShell.notify(); // test
/*try {
Thread.currentThread().sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace();
}*/ // test code, slow the op of self.
ps.println("[Producer " + myId + "] sensed full, begin to wait, Before produce"
+ ", wait at :" + dateFmt.format(Calendar.getInstance().getTime()));
try {
cmShell.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ps.println("[Producer " + myId + "] when sensed full, after wait,"
+ " Currently time is " + dateFmt.format(Calendar.getInstance().getTime()));
}
ps.println("[Producer " + myId + "] do add.");
cmShell.add(Producer.class.getName(), myId);
//cmShell.notify(); // note.//test
System.out.println("[Producer " + myId + "] ===================");
try {
Thread.currentThread().sleep(20);
} catch (InterruptedException e1) {
e1.printStackTrace();
} // test code
System.out.println("[Producer " + myId + "] --------------------");
try {
ps.println("[Producer " + myId + "] normal wait begin.");
cmShell.wait(80);
ps.println("[Producer " + myId + "] normal wait end.");
ps.println("[Producer " + myId + "] normal sleep begin.");
Thread.currentThread().sleep(100);
ps.println("[Producer " + myId + "] normal sleep end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
ps.println("[Producer " + myId + "] once done end.");
}
}
}
}
class Consumer extends Thread {
private static final String simpleFmt = "YYYY-MM-dd hh:mm:ss.sssss";
private final SimpleDateFormat dateFmt = new SimpleDateFormat(simpleFmt);
private final MySyncShell cmShell;
private final PrintStream ps = System.err;
public Consumer(FnyShellMgr fsMgr) {
cmShell = fsMgr.requestAConsumerShell(this.getId());
}
@Override
public void run() {
super.run();
final long myId = getId();
ps.println("[Consumer " + myId + "] run() tid = " + this.getId()
+ ", date = " + dateFmt.format(Calendar.getInstance().getTime()));
ps.println(this.getId() + ", cmShell is " + cmShell);
if (cmShell == null) {
System.err.println("Error, cm shell is null !!");
return;
}
synchronized (cmShell) {
while(true) {
ps.println("[Consumer " + myId + "] once done begin.");
while (cmShell.isEmpty()) {
//ps.println("[Consumer] when sensed empty, begin to notify.");
//cmShell.notify(); // test code
ps.println("[Consumer " + myId + "] when sensed empty, Before usage, wait at :" + dateFmt.format(Calendar.getInstance().getTime()));
try {
cmShell.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ps.println("[Consumer " + myId + "] when sensed empty, after wait, "
+ "Currently time is " + dateFmt.format(Calendar.getInstance().getTime()));
}
ps.println("[Consumer " + myId + "] do use.");
cmShell.use(Consumer.class.getName(), myId);
////cmShell.notify(); // note. test
try {
ps.println("[Consumer " + myId + "] normal wait begin.");
cmShell.wait(50);
ps.println("[Consumer " + myId + "] normal wait end.");
ps.println("[Consumer " + myId + "] normal sleep begin.");
Thread.currentThread().sleep(100);
ps.println("[Consumer " + myId + "] normal sleep end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
ps.println("[Consumer " + myId + "] once done end.");
}
}
}
}
//---------------------------------------------------------------------------------------
// 另外一個文件如下:
package com.spring.test.th;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentHashMap.KeySetView;
public /*final*/ class FnyShellMgr {
private static LinkedList<MySyncShell> slFunnyShells = new LinkedList<MySyncShell>();
static {
MySyncShell shell = new MySyncShell(10);
slFunnyShells.add(shell);
MySyncShell shell2 = new MySyncShell(12);
slFunnyShells.add(shell2);
MySyncShell shell3 = new MySyncShell(15);
slFunnyShells.add(shell3);
}
// N -> 1
private static ConcurrentHashMap<Long, MySyncShell> sKeepingPduMap
= new ConcurrentHashMap<Long, MySyncShell>(/*cap*/);
// Mode2 初期,消費者線程有可能分配到的 shell 沒有生產者,爲了消費者線程不會餓死。所以,後來增加了匹配邏輯。
// N -> 1, 但是要保證至少有 Consumer 對象被匹配。
private static ConcurrentHashMap<Long, MySyncShell> sUsingCsmMap
= new ConcurrentHashMap<Long, MySyncShell>(/*cap*/);
private static synchronized MySyncShell assignAShell(
List <MySyncShell> syncShellList, long custMaskId) {
synchronized (syncShellList) {
if (syncShellList == null || syncShellList.size() <= 0) {
return null;
}
Random randomer = new Random(0xFFFFFFFF);
long rNum = randomer.nextLong();
rNum |= custMaskId;
rNum = rNum % syncShellList.size();
if (rNum < 0) {
rNum = 0 - rNum;
}
final int fIdx = (int)rNum;
return syncShellList.get(fIdx);
}
}
//--------------------------------------------------------------------------
/**
* May return a null value.
* @param maskId
* @return
*/
public synchronized MySyncShell requestAProductShell(final Long maskId) {
if (sKeepingPduMap.contains(maskId)) {
return sKeepingPduMap.get(maskId);
}
// New a map object.
MySyncShell ok2Shell = assignAShell(slFunnyShells, maskId); // funny shell relay.
sKeepingPduMap.put(maskId, ok2Shell);
//...
return ok2Shell;
}
public synchronized void removeAProductShell(final Long maskId) {
sKeepingPduMap.remove(maskId);
}
//--------------------------------------------------------------------------
private Object mCsmLockObj = new Object();
/**
* May return a null value.
* @param maskId
* @return
*/
public MySyncShell requestAConsumerShell(final Long maskId) {
synchronized (mCsmLockObj) {
MySyncShell syncSh = null;
if (sUsingCsmMap.contains(maskId)) {
return sUsingCsmMap.get(maskId);
}
// Share the product's existed map object. // Share mode.
synchronized (sKeepingPduMap) {
int wtCount = 0;
while(sKeepingPduMap.isEmpty()) {
try {
wtCount++;
System.out.println("Customer " + maskId
+ " FIRST requesting shell, wait 100ms start! count " + wtCount);
sKeepingPduMap.wait(100); //
//mCsmLockObj.wait(100); //....
System.out.println("Customer " + maskId + " fst requesting shell, wait end!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Get a object from existed shell or null.
syncSh = getPairObject();
}
if (syncSh != null) {
sUsingCsmMap.put(maskId, syncSh);
}
return syncSh;
}
}
// Implement a simple pair 算法,目前僅做單次匹配。
private MySyncShell getPairObject() {
//return (1 - (250 * 0.01));
//synchronized (sKeepingPduMap) { // assume that already synced when it was called.
// Order the input content.
KeySetView<Long, MySyncShell> ksv = sKeepingPduMap.keySet();
Iterator<Long> ito = ksv.iterator();
// object exception, ex null, do not handle here.
int [] iTemp = new int[ksv.size()];
HashMap<Integer, Long> idx2KeyMap = new HashMap<Integer, Long>();
int ic = 0;
while(ito.hasNext()) {
Long lg = ito.next();
idx2KeyMap.put(ic, lg); // index -> long id of sKeepingPduMap.
MySyncShell shell = sKeepingPduMap.get(lg);
int iMagic = shell.getMaxCapacity();
iTemp[ic++] = iMagic;
}
// create a map.
// 取得最大公約數。
int[] iTemp2 = new int[iTemp.length];
for (int i = 0; i < iTemp.length; i++) { // copy op
if (iTemp[i] <= 0) continue;
iTemp2[i] = iTemp[i];
}
// find the min val. Not negative.
/*int minVal = -1, minIdx = -1;
for (int i = 0; i < iTemp.length; i++) {
if (iTemp[i] <= 0) {
continue;
}
if (minVal == -1) {
minVal = iTemp[i];
minIdx = i;
} else if (minVal > iTemp[i]) {
minVal = iTemp[i];
minIdx = i;
}
}
if (minIdx == -1) {
throw new RuntimeException("Error: wrong input collected data!");
}
int maxProtocal = 1;
for (int j = 2; j <= minVal; j++) {
int total = iTemp.length;
int okC = 0;
for (int k = 0; k < iTemp.length; k++) {
if (iTemp[k] <= 0) {
total--;
continue;
}
if (iTemp[k] % j == 0) {
okC++;
}
}
if (okC == total) {
maxProtocal = j;
}
}
// 縮小 int[] iTemp 裏面的值,暫未驗證,原打算防止加法操作溢出,可能性小;,如果縮小之後,後面還要按比例分配。。
if (maxProtocal > 1) {
for (int i = 0; i < iTemp.length; i++) {
if (iTemp[i] <= 0) continue;
iTemp2[i] = iTemp[i] / maxProtocal;
}
}*/
// Sum values of the array.
int sumVal = 0;
for (int i = 0; i < iTemp.length; i++) {
//System.out.println("iTemp [" + i + "] = " + iTemp[i]);
sumVal += iTemp[i];
}
// set a expand ratio.
double expandRadio = 1.0; //1.2;
final int oriSum = sumVal;
System.out.println("iTemp length " + iTemp.length + "\t"
+ " iTemp2 length " + iTemp2.length);
System.out.println("sumVal -> " + sumVal);
sumVal = (int)Math.ceil(sumVal * expandRadio);
System.out.println("after expand, sumVal -> " + sumVal);
// Create a new map.
int[] iTemp3 = new int[sumVal];
int blankNum = sumVal - oriSum;
int curIdx = 0;
for (int it2 = 0; it2 < iTemp2.length; it2++) {
for (int k = 0; k < iTemp2[it2]; k++) {
iTemp3[curIdx++] = it2; // p key index on iTemp2 -> iTemp -> ito index.
}
}
// init blank index.
for (int lft = curIdx; lft < iTemp3.length; lft++) {
System.out.println("bb: insert a new lft val!! lft = " + lft
+ ", iTemp3 len is " + iTemp3.length);
iTemp3[lft] = -1; // put a invalid value, as a blank val bit.
}
Random rdm = new Random();
final int tIdx = rdm.nextInt(sumVal);
if (iTemp3[tIdx] < 0) { // blank bits
return null;
} else {
int pIdx = iTemp3[tIdx];
//System.err.println("Before error, tidx = " + tIdx + ", pIdx = " + pIdx);
Long tKey = idx2KeyMap.get(Integer.valueOf(pIdx));
MySyncShell shell = sKeepingPduMap.get(tKey);
return shell;
}
}
//}
}
//-------End.