设计模式 Concurrency 之 Half_Sync_Half_Async 半同步半异步模式

该博客探讨了Half_Sync_Half_Async设计模式,旨在平衡同步和异步编程的优缺点。模式在高层采用同步IO,简化编程,而在底层利用异步IO提高效率。主要应用于需要响应外部异步事件和并发执行任务的系统。实现包括同步任务层、队列层和异步任务层,分别负责不同层面的处理工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.动机

  • 同步模式编程简单 但是IO的利用率低
  • 异步模式编程复杂 但是IO利用率高
  • 此模式综合了同步异步的优缺点 高层中使用同步IO模型,简化编程。低层使用异步IO模型 高效执行

2.应用场景

  • 1.系统必须响应和处理外部异步发生的事件
  • 2、一个或多个任务必须在单独的控制线程中执行 其他任务可以在多线程执行
    • 上层任务[数据库查询,文件传输]使用同步IO模式 简化编写并行程序的难度
    • 底层任务[网络控制器的中断处理]使用异步IO模型 提高了执行效率

3.实现方案

  • 1.同步任务层
    • 用户级的进程
    • 本层的任务完成上层的I/O操作,使用同步I/O模型,通过队列层的队列中传输数据。和异步层不同,同步层的任务使用活动对象执行,这些活动对象有自己运行栈和寄存器状态。当执行同步I/O的时候,他们会被阻塞/睡眠。
  • 2.队列层
    • 这个层在同步任务层和异步任务层之间,提供了同步控制和缓存的功能。异步任务的I/O 事件被缓存到消息队列中,同步任务层在队列中提取这些事件(相反方向亦然)
  • 3.异步任务层
    • 处理低层的事件,这些事件由多个外部的事件源产生(例如网卡,终端)。和异步任务不同,此层的实体是被动对象,没有自己的运行栈,要求不能被阻塞。

  • 这里写图片描述

代码实现

AsyncTask:

package com.hqq.concurrency.half_syn_half_async;

import java.util.concurrent.Callable;

/**
 * AsyncTask
 * 封装了一些异步执行的操作和结果。这些操作一般是在后台线程中运行然后将结果通过回调方法传递给调用者
 * Created by heqianqian on 2017/8/9.
 */
public interface AsyncTask<T> extends Callable<T> {

    /**
     * 调用{@link #call()}之前调用 比较大的任务不应该放在此处执行 很有可能会阻塞调用线程
     * 适合一些小的诸如验证的任务
     */
    void onPreCall();

    /**
     * 在{@link #call()}调用之后执行.
     * @param result 程序执行结果
     */
    void onPostCall(T result);

    /**
     * 执行中抛出异常的回调方法
     * 当{@link #call()}或者{@link #onPreCall()}抛出异常时调用
     * @param throwable 异常
     */
    void onError(Throwable throwable);


    /**
     * 调用执行方法
     * @return  返回结果
     * @throws Exception 可能产生的异常
     */
    @Override
    T call() throws Exception;
}

AsynchronousService 

package com.hqq.concurrency.half_syn_half_async;

import java.util.concurrent.*;

/**
 * AsynchronousService
 * 这是一个异步请求层 当有新的请求到达时不会阻塞线程
 * 它将请求传递给同步层 包括一个{@link java.util.concurrent.BlockingQueue}队列和一个线程池 {@link ThreadPoolExecutor}
 * 线程池中的线程在后台同步执行完任务后通过回调方法把结果传递给调用者
 * <p>
 * Created by heqianqian on 2017/8/9.
 */
public class AsynchronousService {

    /**
     * 充当本模式中的同步层 后台执行一些耗时的程序
     */
    private ExecutorService service;

    /**
     * 使用{@code wordQueue}连接同步层和异步层
     * @param workQueue 工作队列
     */
    public AsynchronousService(BlockingQueue<Runnable> workQueue) {
        service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue);
    }

    public <T> void execute(final AsyncTask<T> task){
        try {
            //一些小任务的操作
            task.onPreCall();
        } catch (Exception e) {
            task.onError(e);
            return;
        }
        service.submit(new FutureTask<T>(task){
            @Override
            protected void done() {
                super.done();
                try {
                    task.onPostCall(get());
                } catch (InterruptedException e) {
                } catch (ExecutionException e) {
                    task.onError(e.getCause());
                }
            }
        });
    }

}

App

package com.hqq.concurrency.half_syn_half_async;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * App
 * 半异步/半同步IO模式
 * 适用场景:
 * 1.UNIX网络子系统
 * 2.CORBA
 * 3.安卓异步框架
 * Created by heqianqian on 2017/8/9.
 */
public class App {

    private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

    public static void main(String... args) {
        AsynchronousService service  = new AsynchronousService(new LinkedBlockingQueue<>());

        service.execute(new ArithmeticSumTask(1000));

        service.execute(new ArithmeticSumTask(500));
        service.execute(new ArithmeticSumTask(2000));
        service.execute(new ArithmeticSumTask(1));;
    }

    static class ArithmeticSumTask implements AsyncTask<Long> {

        private long mills;

        public ArithmeticSumTask(long mills) {
            this.mills = mills;
        }

        @Override
        public void onPreCall() {
            if (mills < 0) {
                throw new IllegalArgumentException("mills is less than 0");
            }
        }

        @Override
        public void onPostCall(Long result) {
            LOGGER.info(result.toString());
        }

        @Override
        public void onError(Throwable throwable) {
            throw new IllegalStateException("Should not occur");
        }

        @Override
        public Long call() throws Exception {
            return ap(mills);
        }
    }

    private static long ap(Long mills) {
        try {
            Thread.sleep(mills);
        } catch (InterruptedException e) {
            LOGGER.error("Exception caught.",e);
        }
        return mills * (mills + 1) / 2;
    }
}

<think>嗯...用户这次问得很具体啊,专门询问达梦数据库CONCURRENCY_USER_NUMBER参数。看来是遇到了实际配置问题,或是正在做性能调优。注意到用户之前提供了三个引用片段:第一个讲锁阻塞解决方案,第二个是安装配置说明,第三个提到许可证信息。特别重要的是第三个引用里出现了这个参数的具体查询场景——在v$license视图中能看到。这说明用户很可能是在许可证管理背景下提出问题的。需要重点解释这个参数的核心作用:它不是普通的性能参数,而是许可证控制的核心指标。达梦数据库通过这个参数来限制实际并发用户数,本质上是个授权许可的硬限制。在引用[3]的查询结果里能看到具体数值NULL,说明试用版可能未启用此限制?应该从四个维度展开:1)许可证管理属性(区别于普通配置参数);2)查询方法(v$license视图);3)配置方式(强调需达梦公司授权);4)运维注意事项。特别要提醒用户不要误以为是性能参数——这点在之前引用里完全没提到,容易产生误解。用户可能还隐含想知道:如果并发用户超限会出现什么现象?是否需要重启生效?这些实际运维问题需要主动补充。另外注意到引用[1]提到kill会话的操作,或许可以关联说明:当并发用户超限时,可能需要终止会话来解决。回复时要避免直接复制引用中的LIC_VERSION字段示例,而是提炼出参数查询的本质方法。安装部分(引用[2])暂时用不上,但可以提示参数修改可能需要重新初始化——不过通常许可证参数是热生效的。</think> 达梦数据库中的 `CONCURRENCY_USER_NUMBER` 参数是**许可证控制的核心参数**,用于限制数据库并发用户连接数。其作用、配置及使用说明如下: --- ### **一、参数作用** 1. **许可证限制** - `CONCURRENCY_USER_NUMBER` 是**许可证(License)中定义的硬性限制**,表示数据库允许的**最大并发用户会话数**(非总用户数)。 - 当实际并发连接数超过此值时,新连接请求会被拒绝,并报错 `License limit: max concurrent users exceeded`。 2. **与用户数的区别** - **`AUTHORIZED_USER_NUMBER`**:许可证允许的**总注册用户数**(即用户账户总数)。 - **`CONCURRENCY_USER_NUMBER`**:同一时刻允许的**活跃会话数上限**(与是否注册无关)。 > 📌 **示例**:若 `AUTHORIZED_USER_NUMBER=100` 且 `CONCURRENCY_USER_NUMBER=50`,表示最多可创建100个用户账户,但仅允许50个用户同时在线操作。 --- ### **二、查询参数值** 通过系统视图 `V$LICENSE` 查看许可证信息,包括并发用户限制: ```sql SELECT LIC_VERSION, -- 许可证版本 SERVER_TYPE, -- 服务器类型(如:标准版/企业版) EXPIRED_DATE, -- 许可证过期时间 AUTHORIZED_USER_NUMBER, -- 总授权用户数 CONCURRENCY_USER_NUMBER -- 最大并发用户数 FROM V$LICENSE; ``` [^3] --- ### **三、配置与修改** 1. **非直接修改参数** `CONCURRENCY_USER_NUMBER` **不是普通数据库参数**(如`sp_set_para_value`调整的参数),其值由**许可证文件(.lic)** 定义,需联系达梦公司获取新许可证。 2. **更换许可证步骤**: - **Step 1**: 获取达梦官方提供的新许可证文件(`.lic`)。 - **Step 2**: 停止数据库服务: ```bash systemctl stop DmServiceDMSERVER ``` - **Step 3**: 替换许可证文件(默认路径 `$DM_HOME/bin/dm.lic`)。 - **Step 4**: 重启数据库服务: ```bash systemctl start DmServiceDMSERVER ``` > ⚠️ **注意**:许可证升级需付费,且需匹配数据库版本及硬件信息(`HARDWARE_ID`)[^3]。 --- ### **四、运维建议** 1. **监控并发连接数** 实时检查当前并发会话数: ```sql SELECT COUNT(*) FROM V$SESSIONS WHERE STATE = 'ACTIVE'; ``` 2. **超限处理** - **临时方案**:终止闲置会话(谨慎操作): ```sql -- 查询非活跃会话 SELECT SP_CLOSE_SESSION(sess_id) FROM V$SESSIONS WHERE STATE = 'IDLE' AND last_recv_time < SYSDATE - 30/1440; -- 30分钟无活动 ``` - **根本方案**:升级许可证扩大并发限制[^1][^3]。 --- ### **总结** | **参数** | **性质** | **修改方式** | **关联视图** | |--------------------------|----------------|----------------------|----------------| | `CONCURRENCY_USER_NUMBER`| 许可证硬限制 | 更换达梦许可证文件 | `V$LICENSE` | **关键点**: - 🔒 并发用户数由许可证控制,非数据库配置文件调整。 - 💡 运维中需定期对比 `V$SESSIONS` 活跃会话数与 `V$LICENSE` 的并发限制,避免服务中断。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值