多线程共享资源操作

项目需求

      最近在做一个和支付相关的项目,由于上游通道对每个商户交易额度有上限风控。因此我们需要实现一个商户轮询的机制,通过使用多个商户号,来提高交易上限,满足交易需求。

需求分析

      通过需求分析,我们知道商户的交易额度是共享资源,因此涉及到了共享资源同步的问题,需要我们控制商户的交易额度,及时切换上送的交易商户号,保证在多线程的情况下运行。

功能实现

不说那么多了,直接贴代码。

根据业务需求,设计接口:

public interface IncOrDecInterface {
    /**
     * 恢复额度
     * 当交易失败时,恢复商户可使用的交易额度。
     * @param amount
     */
    boolean inc(Long amount);

    /**
     * 减少额度
     * 交易时,根据交易金额减少商户可使用额度。
     * @param amount
     */
    boolean dec(Long amount);

    /**
     * 更新商户可使用额度
     * 由于商户额度每天更新一次,因此设计此接口,用于重置或更新商户可使用额度。
     * @param amount
     */
    boolean update(Long amount);

    /**
     * 判断对象是否有锁
     */
    boolean isLook();
}

商户对象

public class Merchant implements IncOrDecInterface{
    private static final Logger log = LoggerFactory.getLogger(UnsPayMerchant.class);
    
    private String merchantNo;
    private String signKey;
    private volatile long maxTransactionAmount;

    private Lock lock = new ReentrantLock();
    private boolean isLook = false;

    public String getMerchantNo() {
        return merchantNo;
    }

    public void setMerchantNo(String merchantNo) {
        this.merchantNo = merchantNo;
    }

    public String getSignKey() {
        return signKey;
    }

    public void setSignKey(String signKey) {
        this.signKey = signKey;
    }

    public  long getMaxTransactionAmount() {
        try {
            lock.lock();
            return maxTransactionAmount;
        }finally {
            lock.unlock();
        }
    }

    public void setMaxTransactionAmount(long maxTransactionAmount) {
        this.maxTransactionAmount = maxTransactionAmount;
    }

    @Override
    public boolean inc(Long amount) {
        try{
            lock.lock();
            isLook = true;
            log.info("商户号[{}] 当前额度[{}] 充值金额[{}]",this.merchantNo,this.maxTransactionAmount,amount);
            this.maxTransactionAmount =this.maxTransactionAmount + amount;
            Thread.sleep(1000);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            isLook = false;
        }
        return false;
    }

    @Override
    public boolean dec(Long amount) {
        try{
            lock.lock();
            isLook = true;
            log.info("当前线程[{}] 商户号[{}] 当前额度[{}] 消费金额[{}]",Thread.currentThread().getName(),this.merchantNo ,this.maxTransactionAmount, amount);
            if(this.maxTransactionAmount >= amount){
                this.maxTransactionAmount = this.maxTransactionAmount - amount;
                return true;
            }
        }  finally {
            lock.unlock();
            isLook = false;
        }
        return false;
    }

    @Override
    public boolean update(Long amount) {
        try{
            lock.lock();
            isLook = true;
            log.info("商户号[{}] 当前额度[{}] 恢复额度[{}]", this.merchantNo, this.maxTransactionAmount, amount);
            this.maxTransactionAmount = amount;
            return true;
        }finally {
            lock.unlock();
            isLook = false;
        }
    }

    @Override
    public boolean isLook() {
        return isLook;
    }
}

商户容器

public class MerchantContainer {
    private static final Logger log = LoggerFactory.getLogger(MerchantContainer.class);
    private static ConcurrentLinkedQueue<UnsPayMerchant> unsPayMerchants = new ConcurrentLinkedQueue<>();
    /**
     * 获取商户信息
     * @param amount
     * @return
     */
    public static UnsPayMerchant getMerchant(Long amount) {
        //判断是否有可用额度的商户
        while(unsPayMerchants.size() > 0) {
            for (UnsPayMerchant unsPayMerchant : unsPayMerchants) {
                if(unsPayMerchant.isLook()){
                    continue;
                }else{
                    if(unsPayMerchant.getMaxTransactionAmount() >= amount){
                        unsPayMerchant.dec(amount);
                    }else{
                        unsPayMerchants.remove(unsPayMerchant);
                        continue;
                    }
                }
                return unsPayMerchant;
            }
        }
        return null;
    }

    /**
     * 交易失败恢复商户可用额度
     *
     * @param unsPayMerchantFailure
     * @param amount
     * @return
     */
    public static boolean payFailure(UnsPayMerchant unsPayMerchantFailure, Long amount) {
        for (UnsPayMerchant unsPayMerchant : unsPayMerchants) {
            if (unsPayMerchantFailure.getMerchantNo().equals(unsPayMerchant.getMerchantNo())) {
                if(unsPayMerchant.isLook()){
                    continue;
                }else {
                    unsPayMerchant.inc(amount);
                }
                return true;
            } else {
                unsPayMerchants.add(unsPayMerchantFailure);
            }
        }
        return false;
    }

    /**
     * 更新商户额度
     *
     * @param merchant
     */
    public static void updateMaxAmount(UnsPayMerchant merchant) {
        for (UnsPayMerchant unsPayMerchant : unsPayMerchants) {
            if (unsPayMerchant.getMerchantNo().equals(merchant.getMerchantNo())) {
                unsPayMerchant.update(merchant.getMaxTransactionAmount());
                return;
            }
        }
        log.info("向商户池添加商户[{}] 额度为[{}] 当前商户池有[{}]个商户。", merchant.getMerchantNo(), merchant.getMaxTransactionAmount(), unsPayMerchants.size());
        unsPayMerchants.add(merchant);
    }

测试类

public class UnspayMerchantLoopTest {

    private static Thread[] threads = new Thread[8];

    /**
     * 前置执行任务,准备可以使用的商户数据。
     */
    @Before
    public void init(){
        final UnsPayMerchant unsPayMerchant = new UnsPayMerchant();
        unsPayMerchant.setMerchantNo("101");
        unsPayMerchant.setMaxTransactionAmount(10000);
        UnsPayMerchant unsPayMerchant1 = new UnsPayMerchant();
        unsPayMerchant1.setMerchantNo("102");
        unsPayMerchant1.setMaxTransactionAmount(10000);
        UnsPayMerchant unsPayMerchant2 = new UnsPayMerchant();
        unsPayMerchant2.setMerchantNo("103");
        unsPayMerchant2.setMaxTransactionAmount(10000);

        MerchantContainer.updateMaxAmount(unsPayMerchant);
        MerchantContainer.updateMaxAmount(unsPayMerchant1);
        MerchantContainer.updateMaxAmount(unsPayMerchant2);
    }

    @Test
    public void merLoop() {
        doMerchantPay();
        doPayFailuer();
        doReloadMerchant();
        for (Thread thread : threads) {
            try {
                thread.join();//等待线程执行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 模拟商户交易
     * 启动5个线程,每执行一次休眠6秒。
     */
    private void doMerchantPay(){
        for (int i = 0; i < 5; i++) {
            Thread upLoadThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    Random random = new Random();
                    int maxAmount = 1000;
                    int minAmount = 0;
                    while (true) {
                        UnsPayMerchant merchant = MerchantContainer.getMerchant((long) random.nextInt(maxAmount) % (maxAmount - minAmount + 1) + minAmount);
                        if (merchant == null) {
                            System.out.println(Thread.currentThread().getName() + "额度用完了!");
                            break;
                        }
                        try {
                            Thread.sleep(6000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });
            upLoadThread.start();
            threads[i] = upLoadThread;
        }
    }

    /**
     * 模拟交易失败时,返回商户额度。
     * 启动2个线程,每执行一次休眠5秒。
     */
    private void doPayFailuer(){
        for (int i = 5; i < 7; i++) {
            Thread upLoadThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    String[] strings = new String[]{"101", "102", "103"};
                    Random random = new Random();
                    int maxAmount = 2000;
                    int minAmount = 0;
                    while (true) {
                        for (int i = 0; i < 3; i++) {
                            UnsPayMerchant unsPayMerchant3 = new UnsPayMerchant();
                            unsPayMerchant3.setMerchantNo(strings[i]);
                            MerchantContainer.payFailure(unsPayMerchant3, (long) random.nextInt(maxAmount) % (maxAmount - minAmount + 1) + minAmount);
                        }
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });
            upLoadThread.start();
            threads[i] = upLoadThread;
        }
    }

    /**
     * 恢复商户可用额度
     * 可用于商户每天恢复额度功能。
     * 单线程,每20秒执行一次。
     */
    private void doReloadMerchant(){
        Thread upLoadThread = new Thread(new Runnable() {
            @Override
            public void run() {
                String[] strings = new String[]{"101", "102", "103"};
                Random random = new Random();
                int maxAmount = 2000;
                int minAmount = 0;
                while (true) {
                    for (int i = 0; i < 3; i++) {
                        UnsPayMerchant unsPayMerchant3 = new UnsPayMerchant();
                        unsPayMerchant3.setMerchantNo(strings[i]);
                        unsPayMerchant3.setMaxTransactionAmount(1000);
                        MerchantContainer.updateMaxAmount(unsPayMerchant3);
                    }
                    try {
                        Thread.sleep(20000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
        upLoadThread.start();
        threads[7] = upLoadThread;
    }
}

猜你喜欢

转载自blog.csdn.net/u014075753/article/details/80063683
今日推荐