自定义线程池—学习原理,设计思想,独立实现

2023-11-06

自定义线程池

0.概念与模型

image-20221019151826918


  1. 主线程不断地生产任务
    1. 直接交付线程执行(当前线程池数量 < 核心数)
    2. 否则:加入阻塞任务队列,等到线程池中空闲的线程获取执行
    3. 否则:阻塞队列已满,开发接口(拒绝策略)
      1. 等待/超时等待队列不满时加入队列
      2. 放弃任务
      3. 抛出异常
      4. 调用者/生产者/主线程执行任务
  2. 阻塞队列是双向队列,一端加入任务,一端移除任务
    1. 保证线程安全
    2. 设置多条件
    3. 超时等待和唤醒
      1. 生产者阻塞条件:队列已满
      2. 消费者阻塞条件:队列已空
  3. 线程池
    1. 高效利用多核CPU,核心数 <= 线程数量
    2. 线程复用
      1. 有任务执行
      2. 没有任务超时获取阻塞队列中未执行的任务
      3. 空闲移除线程(保证线程安全)

1.定义任务队列

/**
 * 自定义任务队列
 *
 * @author xzx
 * @date 2022/10/18
 */
@Slf4j
public class BlockingQueue<T> {

    //1.任务队列
    private Deque<T> queue = new ArrayDeque<>();

    //2.锁
    private ReentrantLock lock = new ReentrantLock();

    //3.生产者条件变量
    private Condition fullWaitSet = lock.newCondition();

    //4.消费者条件
    private Condition emptyWaitSet = lock.newCondition();

    //5.队列容量
    private int capacity;


    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 超时等待获取
     * @param timeout
     * @param unit
     * @return
     */
    public T poll(long timeout, TimeUnit unit) {
        lock.lock();
        try {
            long nanos = unit.toNanos(timeout);
            //阻塞获取
            while (queue.isEmpty()) {
                try {
                    if (nanos <= 0) {
                        return null;
                    }
                    //阻塞原因:请求队列为空
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            log.info("从任务队列获取任务成功:{}",t);
            //唤醒因队列已满阻塞的线程
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 阻塞获取
     * @return
     */
    public T take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            log.info("从任务队列获取任务成功:{}",t);
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 存入
     * @param task
     */
    public void put(T task) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                try {
                    log.info("等待加入任务队列: {} ...", task);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("加入任务队列成功: {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 超时存入
     * @param task
     * @param timeout
     * @param timeUnit
     * @return
     */
    public boolean offer(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capacity) {
                try {
                    if (nanos <= 0) {
                        return false;
                    }
                    log.info("等待加入任务队列: {} ...", task);
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("加入任务队列成功: {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 开发获取失败策略
     * @param rejectPolicy 策略
     * @param task 任务
     */
    public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
        lock.lock();
        try {
            if (queue.size() == capacity) {
                log.info("任务队列已满... 执行策略:{}",rejectPolicy);
                rejectPolicy.reject(this, task);
            } else {
                log.info("加入任务队列 {}", task);
                queue.addLast(task);
                emptyWaitSet.signal();
            }
        } finally {
            lock.unlock();
        }
    }
}


2.定义线程池

/**
 * 自定义线程池
 * @author xzx
 * @date 2022/10/18
 */
@Slf4j
public class ThreadPool {

    //任务队列
    private BlockingQueue<Runnable> taskQueue;

    //线程集合
    private HashSet<Worker> workers = new HashSet<>();

    //核心数量
    private int coreSize;

    //超时时间
    private long timeout;

    //单位
    private TimeUnit timeUnit;

    //拒绝策略
    private RejectPolicy<Runnable> rejectPolicy;

    /**
     * 执行任务
     * 1.线程数量 < 核心数量 :新增线程,直接交付任务
     * 2.线程数量 >= 核心数量 : 将任务加入队列,等待线程获取
     * @param task
     */
    public void execute(Runnable task) {
        synchronized (workers) {
            if (workers.size() < coreSize) {
                /**
                 * 直接交付:新建线程执行任务
                 */
                Worker worker = new Worker(task);
                log.info("直接交付--新增线程 worker:{}, 任务 task:{}", worker, task);
                workers.add(worker);
                worker.start();
            } else {
                /**
                 * 暂缓:将任务加入队列
                 */
                // taskQueue.put(task);
                // 1) 死等
                // 2) 带超时等待
                // 3) 让调用者放弃任务执行
                // 4) 让调用者抛出异常
                // 5) 让调用者自己执行任务
                log.info("暂缓交付--加入任务队列 任务 task:{}", task);
                taskQueue.tryPut(rejectPolicy, task);
            }
        }
    }

    /**
     *
     * @param coreSize 核心数目
     * @param timeout 超时时间
     * @param timeUnit  单位
     * @param queueCapacity 任务队列容量
     * @param rejectPolicy 拒绝策略
     */
    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapacity,
                      RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.rejectPolicy = rejectPolicy;
    }

    /**
     * 工作线程
     */
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            //有任务或者从阻塞队列中接取到任务
            while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
                try {
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            //该线程执行完任务就移除
            synchronized (workers) {
                log.info("worker 被移除{}", this);
                workers.remove(this);
            }
        }


    }
}

3.定义拒绝策略接口

/**
 * 自定义线程池
 * @author xzx
 * @date 2022/10/18
 */
@Slf4j
public class ThreadPool {

    //任务队列
    private BlockingQueue<Runnable> taskQueue;

    //线程集合
    private HashSet<Worker> workers = new HashSet<>();

    //核心数量
    private int coreSize;

    //超时时间
    private long timeout;

    //单位
    private TimeUnit timeUnit;

    //拒绝策略
    private RejectPolicy<Runnable> rejectPolicy;

    /**
     * 执行任务
     * 1.线程数量 < 核心数量 :新增线程,直接交付任务
     * 2.线程数量 >= 核心数量 : 将任务加入队列,等待线程获取
     * @param task
     */
    public void execute(Runnable task) {
        synchronized (workers) {
            if (workers.size() < coreSize) {
                /**
                 * 直接交付:新建线程执行任务
                 */
                Worker worker = new Worker(task);
                log.info("直接交付--新增线程 worker:{}, 任务 task:{}", worker, task);
                workers.add(worker);
                worker.start();
            } else {
                /**
                 * 暂缓:将任务加入队列
                 */
                // taskQueue.put(task);
                // 1) 死等
                // 2) 带超时等待
                // 3) 让调用者放弃任务执行
                // 4) 让调用者抛出异常
                // 5) 让调用者自己执行任务
                log.info("暂缓交付--加入任务队列 任务 task:{}", task);
                taskQueue.tryPut(rejectPolicy, task);
            }
        }
    }

    /**
     *
     * @param coreSize 核心数目
     * @param timeout 超时时间
     * @param timeUnit  单位
     * @param queueCapacity 任务队列容量
     * @param rejectPolicy 拒绝策略
     */
    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapacity,
                      RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.rejectPolicy = rejectPolicy;
    }

    /**
     * 工作线程
     */
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            //有任务或者从阻塞队列中接取到任务
            while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
                try {
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            //该线程执行完任务就移除
            synchronized (workers) {
                log.info("worker 被移除{}", this);
                workers.remove(this);
            }
        }


    }
}

4.测试

/**
 * @author xzx
 * @date 2022/10/18
 */
@Slf4j
public class Test {

    public static void main(String[] args) {
        ThreadPool threadPool = new ThreadPool(4,
                1000, TimeUnit.MILLISECONDS, 2, (queue, task) -> {
            // 1. 死等
            // queue.put(task);
            // 2) 带超时等待
            // queue.offer(task, 1500, TimeUnit.MILLISECONDS);
            // 3) 让调用者放弃任务执行
            // log.info("放弃{}", task);
            // 4) 让调用者抛出异常
            // throw new RuntimeException("任务执行失败 " + task);
            // 5) 让调用者自己执行任务
            log.info("让调用者自己执行任务");
            task.run();
        });
        for (int i = 0; i < 20; i++) {
            int j = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("任务执行成功: {}", j);
            });
        }
    }
}

控制台打印日志

15:11:41.887 [main] INFO com.example.juc.threadpool.ThreadPool - 直接交付--新增线程 worker:Thread[Thread-0,5,main], 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@66cd51c3
15:11:41.890 [main] INFO com.example.juc.threadpool.ThreadPool - 直接交付--新增线程 worker:Thread[Thread-1,5,main], 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@5fcfe4b2
15:11:41.890 [main] INFO com.example.juc.threadpool.ThreadPool - 直接交付--新增线程 worker:Thread[Thread-2,5,main], 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@5eb5c224
15:11:41.890 [main] INFO com.example.juc.threadpool.ThreadPool - 直接交付--新增线程 worker:Thread[Thread-3,5,main], 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@73a8dfcc
15:11:41.891 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@7e774085
15:11:41.891 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@7e774085
15:11:41.891 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@3f8f9dd6
15:11:41.891 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@3f8f9dd6
15:11:41.891 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@aec6354
15:11:41.891 [main] INFO com.example.juc.threadpool.BlockingQueue - 任务队列已满... 执行策略:com.example.juc.threadpool.Test$$Lambda$1/683287027@1c655221
15:11:41.891 [main] INFO com.example.juc.threadpool.Test - 让调用者自己执行任务
15:11:42.391 [Thread-1] INFO com.example.juc.threadpool.Test - 任务执行成功: 1
15:11:42.391 [Thread-0] INFO com.example.juc.threadpool.Test - 任务执行成功: 0
15:11:42.391 [Thread-2] INFO com.example.juc.threadpool.Test - 任务执行成功: 2
15:11:42.406 [main] INFO com.example.juc.threadpool.Test - 任务执行成功: 6
15:11:42.406 [Thread-3] INFO com.example.juc.threadpool.Test - 任务执行成功: 3
15:11:42.406 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@58d25a40
15:11:42.406 [Thread-1] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@7e774085
15:11:42.406 [Thread-0] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@3f8f9dd6
15:11:42.406 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@58d25a40
15:11:42.406 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@1b701da1
15:11:42.406 [Thread-2] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@58d25a40
15:11:42.406 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@1b701da1
15:11:42.406 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@726f3b58
15:11:42.406 [Thread-3] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@1b701da1
15:11:42.406 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@726f3b58
15:11:42.406 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@442d9b6e
15:11:42.406 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@442d9b6e
15:11:42.406 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@ee7d9f1
15:11:42.406 [main] INFO com.example.juc.threadpool.BlockingQueue - 任务队列已满... 执行策略:com.example.juc.threadpool.Test$$Lambda$1/683287027@1c655221
15:11:42.406 [main] INFO com.example.juc.threadpool.Test - 让调用者自己执行任务
15:11:42.920 [Thread-2] INFO com.example.juc.threadpool.Test - 任务执行成功: 7
15:11:42.920 [Thread-0] INFO com.example.juc.threadpool.Test - 任务执行成功: 5
15:11:42.920 [main] INFO com.example.juc.threadpool.Test - 任务执行成功: 11
15:11:42.920 [Thread-1] INFO com.example.juc.threadpool.Test - 任务执行成功: 4
15:11:42.920 [Thread-3] INFO com.example.juc.threadpool.Test - 任务执行成功: 8
15:11:42.920 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@15615099
15:11:42.920 [Thread-2] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@726f3b58
15:11:42.920 [Thread-0] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@442d9b6e
15:11:42.920 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@15615099
15:11:42.920 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@1edf1c96
15:11:42.920 [Thread-1] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@15615099
15:11:42.920 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@1edf1c96
15:11:42.920 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@368102c8
15:11:42.920 [Thread-3] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@1edf1c96
15:11:42.920 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@368102c8
15:11:42.920 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@6996db8
15:11:42.920 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@6996db8
15:11:42.920 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@1963006a
15:11:42.920 [main] INFO com.example.juc.threadpool.BlockingQueue - 任务队列已满... 执行策略:com.example.juc.threadpool.Test$$Lambda$1/683287027@1c655221
15:11:42.920 [main] INFO com.example.juc.threadpool.Test - 让调用者自己执行任务
15:11:43.434 [main] INFO com.example.juc.threadpool.Test - 任务执行成功: 16
15:11:43.434 [Thread-3] INFO com.example.juc.threadpool.Test - 任务执行成功: 13
15:11:43.434 [Thread-0] INFO com.example.juc.threadpool.Test - 任务执行成功: 10
15:11:43.434 [Thread-1] INFO com.example.juc.threadpool.Test - 任务执行成功: 12
15:11:43.434 [Thread-3] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@368102c8
15:11:43.434 [Thread-2] INFO com.example.juc.threadpool.Test - 任务执行成功: 9
15:11:43.434 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@7fbe847c
15:11:43.434 [Thread-0] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@6996db8
15:11:43.434 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@7fbe847c
15:11:43.434 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@41975e01
15:11:43.434 [Thread-1] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@7fbe847c
15:11:43.434 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@41975e01
15:11:43.434 [main] INFO com.example.juc.threadpool.ThreadPool - 暂缓交付--加入任务队列 任务 task:com.example.juc.threadpool.Test$$Lambda$2/553264065@c2e1f26
15:11:43.434 [Thread-2] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@41975e01
15:11:43.434 [main] INFO com.example.juc.threadpool.BlockingQueue - 加入任务队列 com.example.juc.threadpool.Test$$Lambda$2/553264065@c2e1f26
15:11:43.946 [Thread-1] INFO com.example.juc.threadpool.Test - 任务执行成功: 17
15:11:43.946 [Thread-0] INFO com.example.juc.threadpool.Test - 任务执行成功: 15
15:11:43.946 [Thread-3] INFO com.example.juc.threadpool.Test - 任务执行成功: 14
15:11:43.946 [Thread-2] INFO com.example.juc.threadpool.Test - 任务执行成功: 18
15:11:43.946 [Thread-1] INFO com.example.juc.threadpool.BlockingQueue - 从任务队列获取任务成功:com.example.juc.threadpool.Test$$Lambda$2/553264065@c2e1f26
15:11:44.458 [Thread-1] INFO com.example.juc.threadpool.Test - 任务执行成功: 19
15:11:44.953 [Thread-0] INFO com.example.juc.threadpool.ThreadPool - worker 被移除Thread[Thread-0,5,main]
15:11:44.953 [Thread-2] INFO com.example.juc.threadpool.ThreadPool - worker 被移除Thread[Thread-2,5,main]
15:11:44.953 [Thread-3] INFO com.example.juc.threadpool.ThreadPool - worker 被移除Thread[Thread-3,5,main]
15:11:45.463 [Thread-1] INFO com.example.juc.threadpool.ThreadPool - worker 被移除Thread[Thread-1,5,main]

Process finished with exit code 0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义线程池—学习原理,设计思想,独立实现 的相关文章

随机推荐

  • flutter加载不同分辨率本地图片

    flutter移动开发怎么加载本地图片 首先在该项目根目录也就是和ios android同级创建一个images文件夹用来存放图片资源 然后放入需要加载的图片资源例如ic phone png 然后在项目目录下找到pubspec yaml文件
  • 【定量分析、量化金融与统计学】统计推断基础 番外(1)---T table与Z table的值

    目录 一 前言 二 T table 三 Z table 一 前言 为了方便之后的例题讲解 这里放上T tabel和Z table的值 怎么查表 本篇中会直接讲 所以这里就只看表格就行 本篇为工具篇 二 T table 我们给两个版本 适合用
  • Redis学习笔记:数据结构和命令

    本文是自己的学习笔记 主要参考资料如下 马士兵 4 Redis的五大数据类型 1 1 String 1 1 1 String 类型的命令 1 1 2 存储对象 1 2 List 1 2 1 List基本命令 1 2 2 List高级命令 1
  • linux 提高文件读写速度 mmap,【EA字符串Linux面试题】面试问题:kafka读写… - 看准网...

    传统IO 缓存IO 传统IO也就是缓存IO 数据先从磁盘复制到内核空间缓冲区 然后从内核空间缓冲区复制到应用程序的地址空间 这里的内核缓冲区也就是页缓存 PageCache 是虚拟内存空间 读操作 操作系统检查内核的缓冲区有没有需要的数据
  • QT结构体中定义QString注意点

    当需要进行多进程通讯时 结构体中出现字符串时尽量采用C 标准类型 尽量少用QT特有类型QString字符串 尽量采用char 类型替代 这样在多进程通讯时 可直接通过memcpy直接复制内存的方式 而不用担心内存异常问题 由于QString
  • 动手搭建第一个小程序音视频Demo

    欢迎大家前往云 社区 获取更多腾讯海量技术实践干货哦 作者 小程序音视频产品经理 腾讯云提供了全套技术文档和源码来帮助您快速构建一个音视频小程序 但是再好的源码和文档也有学习成本 为了尽快的能调试起来 我们还提供了一个免费的一键部署服务 您
  • 华为OD机试 - 最长连续子序列(Java )

    题目描述 有N个正整数组成的一个序列 给定整数sum 求长度最长的连续子序列 使他们的和等于sum 返回此子序列的长度 如果没有满足要求的序列 返回 1 输入描述 第一行输入是 N个正整数组成的一个序列 第二行输入是 给定整数sum 输出描
  • 02 电阻容模型的创建

    打开状态栏 画电阻 电容的封装 实操要点 1 SCH Library一定要先选中 出现元件库的列表 2 放置完元件可以按ESC取消 3 Ctrl C V可以复制粘贴用 4 多余的线可以使用Delete删除 5 可以按鼠标右键轻微的拖动屏幕
  • ViLT:最简单的多模态Transformer

    原文链接 感谢原作者 ViLT 最简单的多模态Transformer 陀飞轮 复
  • 两台外网计算机远程桌面访问(内网穿透)

    背景 如图所示 项目中需要远程访问项目现场的外网计算机 通过外网计算机再访问到现场内网环境中的另外一台计算机 原计划通过市面上的远程桌面软件 如向日葵 ToDesk AnyDesk等 建立两台外网计算机的远程连接 在使用windows自带的
  • UmiJS介绍--mock(四)

    umi 里约定 mock 文件夹下的文件即 mock 文件 文件导出接口定义 支持基于 require 动态分析的实时刷新 支持 ES6 语法 以及友好的出错提示 1 引入 Mock js Mock js是常用的辅助生成模拟数据的第三方库
  • 编写过滤器jar包并植入到项目中

    公司有项目有个需求 就是希望可以写一个统一的权限管理 每次开发新项目的时候 可以通过添加依赖包进行权限的获取 验证 至于为什么不使用aop 拦截器二使用过滤器 是因为在java中 如果3者同事存在 最先执行的是过滤器 一 新建第三方过滤器j
  • QT 使用 qtasome图标 (python版)

    首先安装 qtawesome 库 然后到图标库找到需要的图标 图标名称为 fa xxx 图标库链接 http www fontawesome com cn faicons 在 retranslateUi 模块中 对相应 按钮 进行操作 运行
  • 6_机器翻译与Seq2Seq模型

    文章目录 一 Sequence to Sequence Model Seq2Seq 1 1 Machine Translation Data 机器翻译数据 1 2 Tokenization Build Dictionary 分词和建造字典
  • uva 1601 The Morning after Halloween code2

    题目 The Morning after Halloween 题意 有n个用小写字母表示的鬼和一张地图 每个鬼都要移动到对应的大写字母 两个鬼的位置不能在一次移动中交换 问最少步数 思路 双向bfs 此题还可以单向bfs 见code1 1
  • 0.面向对象的设计模式与原则

    面向对象的设计模式与原则 设计模式与面向对象 面向对象编程语言的三大机制 面向对象编程语言 OOPL 并非面向对象的全部 重新认识面向对象 从设计原则到设计模式 几条更具体的设计原则 1 单一职责原则 SRP 2 里式替换原则 LSP 3
  • 北京大学肖臻老师《区块链技术与应用》公开课笔记16 The Dao

    这个是接着北京大学肖臻老师的课记的笔记 由于前面的笔记在 https blog csdn net Mu Xiaoye article details 104299664 已经有前人写好了 并且写的不错 所以这里直接给出链接 然后这个是剩下的
  • 通过主机名字获取与端口号获取对应的IP地址

    利用boost asio中的库函数获取本地主机的名字和通过主机名字获取与端口号获取对应的IP地址 源代码如下 Boost others network function cpp 此文件包含 main 函数 程序执行将在此处开始并结束 inc
  • C++ 默认析构函数

    1 与构造函数一样 假如我们不指定自己的析构函数 那么编译器将为我们创建一个默认析构函数 析构函数 Destructor 也是一种特殊的成员函数 没有返回值 不需要用户调用 而是在销毁对象时自动执行 与构造函数不同的是 析构函数的名字是在类
  • 自定义线程池—学习原理,设计思想,独立实现

    自定义线程池 0 概念与模型 主线程不断地生产任务 直接交付线程执行 当前线程池数量 lt 核心数 否则 加入阻塞任务队列 等到线程池中空闲的线程获取执行 否则 阻塞队列已满 开发接口 拒绝策略 等待 超时等待队列不满时加入队列 放弃任务