多生產者-多消費者 例子2 線程協同

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.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章