java并发编程 13:JUC之Semaphore、CountdownLatch、 CyclicBarrier

news2024/11/26 16:37:18

目录

  • Semaphore
    • 使用
    • 常见应用
    • 原理
      • 源码
      • 流程
  • CountdownLatch
    • 使用
    • 原理
  • CyclicBarrier
    • 使用

Semaphore

使用

Semaphore是一种计数信号量,它用于控制对共享资源的访问。它维护了一个许可计数器,表示可用的许可数量。线程在访问共享资源前必须先获得许可,如果许可数量大于0,则线程可以获得许可并继续执行,同时许可数量减少;如果许可数量为0,则线程必须等待,直到有其他线程释放许可。

Semaphore的构造函数有两种形式:

  1. Semaphore(int permits): 创建具有给定许可数量的Semaphore对象。
  2. Semaphore(int permits, boolean fair): 创建具有给定许可数量的Semaphore对象,并指定是否使用公平的排序策略。如果fair为true,则等待时间较长的线程优先获得许可。

Semaphore类的主要方法有:

  1. void acquire(): 获取一个许可,如果没有可用许可,则线程将阻塞,直到有可用许可为止。
  2. void acquire(int permits): 获取给定数量的许可,如果没有足够的可用许可,则线程将阻塞,直到有足够数量的许可为止。
  3. void release(): 释放一个许可,将许可数量加1。
  4. void release(int permits): 释放给定数量的许可,将许可数量加上指定数量。
  5. int availablePermits(): 返回当前可用的许可数量。
  6. boolean tryAcquire(): 尝试获取一个许可,如果有可用许可,则立即返回true,否则返回false。
  7. boolean tryAcquire(int permits): 尝试获取给定数量的许可,如果有足够的可用许可,则立即返回true,否则返回false。
  8. void acquireUninterruptibly(): 获取一个许可,不响应中断。
  9. void acquireUninterruptibly(int permits): 获取给定数量的许可,不响应中断。

示例:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        // 创建一个Semaphore对象,初始许可数量为3
        Semaphore semaphore = new Semaphore(3);

        // 创建10个线程并启动
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Worker(semaphore, i));
            thread.start();
        }
    }

    static class Worker implements Runnable {
        private final Semaphore semaphore;
        private final int id;

        public Worker(Semaphore semaphore, int id) {
            this.semaphore = semaphore;
            this.id = id;
        }

        @Override
        public void run() {
            try {
                System.out.println("Worker " + id + " is waiting for a permit.");
                semaphore.acquire();
                System.out.println("Worker " + id + " got a permit and is doing some work.");
                Thread.sleep(2000);
                System.out.println("Worker " + id + " released the permit and finished.");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个示例中,我们创建了一个Semaphore对象,并初始化许可数量为3。然后,我们创建了10个Worker线程,每个线程在执行前会先尝试获取一个许可,如果没有可用许可,它将会被阻塞直到有可用许可为止。每个Worker线程执行一段模拟的工作,然后释放许可,以便其他线程可以继续执行。

请注意,Semaphore适用于控制对有限资源的并发访问。通过适当地设置许可数量,您可以控制同时访问共享资源的线程数量,从而避免资源过度竞争和冲突。

常见应用

Semaphore在实际应用中有很多用途,它是一种非常有用的并发控制工具。下面列举一些Semaphore的实际应用:

  1. 连接池管理:在数据库连接池、线程池等资源池管理中,Semaphore可以控制可用资源的数量,限制同时处理的连接或线程数量,避免资源过度使用。

  2. 限流控制:在网络应用中,可以使用Semaphore实现流量控制,限制请求的处理速率,防止系统过载。

  3. 并发访问控制:在某些场景下,需要限制同时访问某个资源的线程数量,例如限制文件的并发读写、限制同时下载文件的数量等。

  4. 多任务协作:在一些多阶段任务中,需要控制各个阶段的并发执行,Semaphore可以用来实现阶段间的同步。

  5. 实现有界容器:例如使用Semaphore实现有界的阻塞队列,控制队列中元素的数量,防止内存溢出或者资源耗尽。

  6. 实现读写锁:Semaphore也可以用来实现读写锁的功能,用于读操作和写操作之间的互斥。

  7. 任务并行处理:在一些并行计算场景中,可以使用Semaphore来控制并行处理的任务数量,控制计算资源的使用。

  8. 分布式系统协调:在分布式系统中,Semaphore可以用来实现资源的分配和协调,保证分布式任务的有序执行。

这些仅是Semaphore的一部分应用场景,实际上,Semaphore是一种非常灵活和强大的工具,可以用于解决许多并发编程中的问题。在实际使用中,需要根据具体场景和需求合理地配置Semaphore的许可数量,从而达到控制并发的目的。

原理

源码

  1. 构造方法

构造方法如下:

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

构造方法里有个必须的参数permits,用来表示信号量的个数,第二可选参数表示公平还是非公平。

构造方法里会初始化一个同步器sync

先看下默认的非公平的NonfairSync,代码如下:

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = -2694183684443567898L;

    NonfairSync(int permits) {
        super(permits);
    }

    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
}

然后看下继承的内部的Sync类:

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;

    Sync(int permits) {
        setState(permits);
    }

    final int getPermits() {
        return getState();
    }

    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            int next = current + releases;
            if (next < current) // overflow
                throw new Error("Maximum permit count exceeded");
            if (compareAndSetState(current, next))
                return true;
        }
    }

    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }

    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}

可以看到:参数permits又在调用AQS的setState方法时,作为方法参数,也就是传的permits参数称为了我们前面说的AQS的state参数。

  1. 获取锁

下面看下获取锁的代码。

首先是acquire方法:

public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

然后看下acquireSharedInterruptibly:

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
  			// 如果尝试获取许可证后,剩余数量小于0,则执行下面的方法,否则就是获取成功了
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

然后调用tryAcquireShared方法,找到对应的实现类:

protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}

nonfairTryAcquireShared代码如下:

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
      	// 拿到permits,即这个信号量可用的许可证数量
        int available = getState();
      	// 可用的减去获取的,即剩余的许可证
        int remaining = available - acquires;
      	// 如果剩余的小于0。或者大于0且使用CAS修改available成功,则返回剩余的数量
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

nonfairTryAcquireShared方法返回值在acquireSharedInterruptibly方法里,做了一个判断如果小于0,还要执行doAcquireSharedInterruptibly方法:

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

上面的方法前面也已经讲解过,主要就是把后面获取不到锁的节点加入到队列中等待。

  1. 释放锁
public void release() {
    sync.releaseShared(1);
}

看下releaseShared方法:

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

然后是tryReleaseShared方法,找到对应实现方法:

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
      	// 拿到当前的状态值
        int current = getState();
      	// 当前的 + 要释放的,即新的释放锁后的状态值
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
      	// CAS修改状态值,成功返回true
        if (compareAndSetState(current, next))
            return true;
    }
}

上面方法如果返回ture,在releaseShared方法里还要执行doReleaseShared方法:

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

这个方法前面也讲过类似,主要是唤醒等待队列中的节点。

流程

Semaphore 有点像一个停车场,permits 就好像停车位数量,当线程获得了 permits 就像是获得了停车位,然后停车场显示空余车位减一。

刚开始,permits(state)为 3,这时 5 个线程来获取资源

在这里插入图片描述

假设其中 Thread-1,Thread-2,Thread-4 cas 竞争成功,而 Thread-0 和 Thread-3 竞争失败,进入 AQS 队列park 阻塞

在这里插入图片描述

这时 Thread-4 释放了 permits,状态如下

在这里插入图片描述

接下来 Thread-0 竞争成功,permits 再次设置为 0,设置自己为 head 节点,断开原来的 head 节点,unpark 接下来的 Thread-3 节点,但由于 permits 是 0,因此 Thread-3 在尝试不成功后再次进入 park 状态
在这里插入图片描述

CountdownLatch

使用

CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法之后的语句。

CountDownLatch 主要有两个方法:

  • await:当一个或多个线程调用 await 方法时,这些线程会阻塞;
  • countDown:其它线程调用 countDown 方法会将计数器减 1(调用 countDown 方法的线程不会阻塞),当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行。

示例:

package up.cys.chapter13;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

@Slf4j
public class CountdownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        new Thread(() -> {
            log.info("begin...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        }).start();
        new Thread(() -> {
            log.info("begin...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        }).start();
        new Thread(() -> {
            log.info("begin...");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        }).start();
        log.info("waiting...");
        latch.await();
        log.info("wait end...");
    }
}

运行结果如下:

2023-07-26 21:24:12,866 - 0    INFO  [Thread-2] up.cys.chapter13.CountdownLatchTest:37  - begin...
2023-07-26 21:24:12,866 - 0    INFO  [Thread-1] up.cys.chapter13.CountdownLatchTest:27  - begin...
2023-07-26 21:24:12,866 - 0    INFO  [main] up.cys.chapter13.CountdownLatchTest:46  - waiting...
2023-07-26 21:24:12,866 - 0    INFO  [Thread-0] up.cys.chapter13.CountdownLatchTest:17  - begin...
2023-07-26 21:24:13,884 - 1018 INFO  [Thread-0] up.cys.chapter13.CountdownLatchTest:24  - end...2
2023-07-26 21:24:14,375 - 1509 INFO  [Thread-2] up.cys.chapter13.CountdownLatchTest:44  - end...1
2023-07-26 21:24:14,880 - 2014 INFO  [Thread-1] up.cys.chapter13.CountdownLatchTest:34  - end...0
2023-07-26 21:24:14,880 - 2014 INFO  [main] up.cys.chapter13.CountdownLatchTest:48  - wait end...

可使用线程池来改进,如下:

package up.cys.chapter13;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class CountdownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        ExecutorService service = Executors.newFixedThreadPool(4);
        service.submit(() -> {
            log.info("begin...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        });
        service.submit(() -> {
            log.info("begin...");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        });
        service.submit(() -> {
            log.info("begin...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
            log.info("end...{}", latch.getCount());
        });
        service.submit(()->{
            try {
                log.info("waiting...");
                latch.await();
                log.info("wait end...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

运行结果如下:

2023-07-26 21:29:47,937 - 0    INFO  [pool-1-thread-1] up.cys.chapter13.CountdownLatchTest:20  - begin...
2023-07-26 21:29:47,939 - 2    INFO  [pool-1-thread-4] up.cys.chapter13.CountdownLatchTest:51  - waiting...
2023-07-26 21:29:47,938 - 1    INFO  [pool-1-thread-3] up.cys.chapter13.CountdownLatchTest:40  - begin...
2023-07-26 21:29:47,937 - 0    INFO  [pool-1-thread-2] up.cys.chapter13.CountdownLatchTest:30  - begin...
2023-07-26 21:29:48,959 - 1022 INFO  [pool-1-thread-1] up.cys.chapter13.CountdownLatchTest:27  - end...2
2023-07-26 21:29:49,456 - 1519 INFO  [pool-1-thread-2] up.cys.chapter13.CountdownLatchTest:37  - end...1
2023-07-26 21:29:49,956 - 2019 INFO  [pool-1-thread-3] up.cys.chapter13.CountdownLatchTest:47  - end...0
2023-07-26 21:29:49,956 - 2019 INFO  [pool-1-thread-4] up.cys.chapter13.CountdownLatchTest:53  - wait end...

原理

  1. CountdownLatch 类声明

CountdownLatch 是一个具有以下构造函数和方法的类:

public class CountdownLatch {
    public CountdownLatch(int count) { ... }

    public void await() throws InterruptedException { ... }

    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { ... }

    public void countDown() { ... }

    public long getCount() { ... }
}
  1. 构造函数

CountdownLatch 的构造函数接受一个整数参数 count,表示需要等待的事件数量。这个 count 参数在创建 CountdownLatch 实例时被初始化,并且一旦初始化后,就不能再更改。

  1. await() 方法

await() 方法使调用线程等待,直到计数器变为零。如果计数器当前为零,则此方法立即返回。如果计数器大于零,则线程将被阻塞,直到另一个线程通过调用 countDown() 方法将计数器减少为零。

  1. await(long timeout, TimeUnit unit) 方法

await(long timeout, TimeUnit unit) 方法与 await() 方法类似,但是允许设置一个超时时间。如果在指定的时间内计数器变为零,该方法将返回 true,否则返回 false

  1. countDown() 方法

countDown() 方法将 CountdownLatch 的计数器减少1。如果计数器减少到零,所有在 await() 方法上等待的线程将被释放,并且它们将继续执行。

  1. getCount() 方法

getCount() 方法返回当前计数器的值。

CyclicBarrier

使用

CyclicBarrier 是 Java 并发包 (java.util.concurrent) 中提供的一种线程同步机制,它允许一组线程相互等待,直到所有线程都到达某个共同点,然后再同时继续执行后续操作。CyclicBarrier 的工作方式类似于一组线程相互等待集合齐全后再同时出发,就像是一个循环屏障。

特点:

  1. 循环性:与 CountdownLatch 不同,CyclicBarrier 可以被重用,即在一次等待完成后,它的计数器会被重置,可以再次使用。
  2. 等待点:所有线程都必须在 CyclicBarrier 中声明的等待点上等待,直到最后一个线程到达这个点,所有等待的线程将被释放。
  3. 计数器CyclicBarrier 内部维护一个计数器,用于跟踪还有多少个线程未到达等待点。

CyclicBarrier 的构造函数如下:

public CyclicBarrier(int parties, Runnable barrierAction)
  • parties 表示等待的线程数,即需要同步的线程数量。每个线程调用 await() 方法时,都会将计数器减1,直到计数器减至0时,所有等待的线程将被释放,继续执行后续操作。
  • barrierAction 是一个可选的 Runnable 参数,在所有线程释放后,将在最后一个线程到达等待点时执行。

使用示例:

下面是一个简单的例子,说明如何使用 CyclicBarrier

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Example {
    public static void main(String[] args) {
        int numberOfThreads = 3;

        // 定义一个Runnable,用于在所有线程释放后执行的操作
        Runnable barrierAction = () -> System.out.println("All threads have reached the barrier.");

        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, barrierAction);

        for (int i = 0; i < numberOfThreads; i++) {
            Thread thread = new Thread(() -> {
                try {
                    // 模拟一些任务执行
                    System.out.println("Task executed by thread: " + Thread.currentThread().getName());
                    barrier.await(); // 等待其他线程
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}

在上面的示例中,我们创建了3个线程,每个线程都模拟执行一个任务,并在完成后调用 await() 方法等待其他线程。当所有线程都到达等待点时,barrierAction 中的操作会被执行,输出 “All threads have reached the barrier.”。

这就是 CyclicBarrier 的简单介绍和用法。它在一些多线程协作的场景中非常有用,例如在某个任务需要多个子任务都完成后再进行处理的情况下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/794144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小学期笔记——天天酷跑2

程序的錯誤一共是3個維度 1&#xff1a;編譯錯誤 &#xff08;1&#xff09;点击后边的红色点点 &#xff08;2&#xff09;鼠标移动红线上 &#xff08;3&#xff09;复制错误提示内容 &#xff08;4&#xff09;粘贴到CHATGPT (5) 复制爆红正行&#xff08;全文&#xff0…

JavaScript function默认参数赋值前后顺序差异

1、(num1,num2num1) 当传值仅传一个参数时&#xff0c;先给到第一个参数即num1&#xff0c;num1再赋值给num2&#xff0c; function sum(num1, num2 num1) {console.log(num1 num2) } sum(10)//20 sum(10,3)//13 2、(t2t1,t1) 当传值仅有一个参数时&#xff0c;先给到第一个…

Spring Boot OAuth2 认证服务器搭建及授权码认证演示

本篇使用JDK版本是1.8,需要搭建一个OAuth 2.0的认证服务器,用于实现各个系统的单点登录。 框架构思 这里选择Spring Boot+Spring Security + Spring Authorization Server 实现,具体的版本选择如下: Spirng Boot 2.7.14 , Spring Boot 目前的最新版本是 3.1.2,在官方的介…

华为eNSP:isis配置跨区域路由

一、拓扑图 二、路由器的配置 1、配置接口IP AR1: <Huawei>system-view [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei-GigabitEthernet0/0/0]q AR2: [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.2 24 [Huawe…

【弹力设计篇】聊聊限流设计

为什么需要限流 对于一个后端系统来说&#xff0c;其处理能力是有限的&#xff0c;会受到业务流程&#xff0c;系统配置等的限制&#xff0c;QPS和TPS有一个上限值&#xff0c;比如一个订单系统1分钟可以处理100个请求。当1分钟超过100个请求的时候&#xff0c;我们为了保证系…

13.2.2 【Linux】使用者功能

不论是 useradd/usermod/userdel &#xff0c;那都是系统管理员所能够使用的指令&#xff0c; 如果我是一般身份使用者&#xff0c;那么我是否除了密码之外&#xff0c;就无法更改其他的数据呢&#xff1f; 当然不是啦&#xff01;这里我们介绍几个一般身份使用者常用的帐号数据…

Jmeter基础篇(17)Jmeter中Stop和X的区别

一、前言 在Apache JMeter中&#xff0c;Stop和X之间存在一些区别。虽然它们都是用于结束测试的不同方法&#xff0c;但它们在实施方式和效果上存在一些差异。 二、Jmeter中的Stop 首先&#xff0c;让我们了解一下Stop。 在JMeter中&#xff0c;Stop是指在测试结束时关闭线…

Java毕业设计—爱宠医院管理系统设计与实现

爱宠医院管理系统 获取数论文、代码、答辩PPT、安装包&#xff0c;可以查看文章底部 一、 如何安装及配置环境 要运行整个爱宠医院管理系统需要安装数据库&#xff1a;MySQL 5.5&#xff0c;开发工具&#xff1a;JDK 1.8&#xff0c;开发语开发平台&#xff1a;Eclipse&…

Java开发中的分层开发和整洁架构

分层开发(横向拆分) 分层开发的概念: maven多模块开发项目管理.可以利用这种管理功能,实现一个项目的多层次模块开发–分层开发. 比如,当前项目HelloController依赖HelloService 这样做目的: 复杂开发过程.解耦(不调整依赖关系,无法解耦).分层开发(横向拆分)和纵向拆分的区别…

通过问题解决者手册推动你的结果 - 提高思维能力的 17 种方法

大部分人对产品管理的理解都是解决问题&#xff0c;这是他们的主要工作——找出客户的问题是什么并解决它们。但现在&#xff0c;热衷于解决问题的问题是&#xff0c;当我们看到问题时&#xff0c;本能反应是“我该如何解决它&#xff1f;” 这意味着&#xff1a;当我试图自己解…

26道Android 常规题,不会还不知吧?

目录 1.如何对 Android 应用进行性能分析 2.什么情况下会导致内存泄露 3.如何避免 OOM 异常 4.Android 中如何捕获未捕获的异常 5.ANR 是什么&#xff1f;怎样避免和解决 ANR&#xff08;重要&#xff09; 6.Android 线程间通信有哪几种方式 7.Devik 进程&#xff0c;lin…

今年这情况。。。大家还是多做一手准备吧

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;又得准备面试了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约时间&a…

外观模式-提供统一入口

在办理流量套餐的时候&#xff0c;我们可以去官网查查询套餐&#xff0c;找出符合我们需求的套餐&#xff0c;然后进行办理。官网是充斥着各种各样的套餐&#xff0c;如果我们一个个去查看这些套餐&#xff0c;耗费的时间很多。而且在办理套餐的时候&#xff0c;步骤也比较多。…

提高公文写作效率,可以采用模板和样例来辅助写作

采用模板和样例是提高公文写作效率的一种常见方法。 模板是指已经制作好的公文格式和结构模板&#xff0c;可以根据模板来组织和排版自己的文章&#xff0c;以减少排版时间和排版错误。常见的模板包括各类公文格式&#xff0c;例如通知、报告、请示等等。在使用模板的过程中&am…

[MyBatis] Invalid bound statement (not found)解决方案

这个问题是由于mapper接口和mapper映射文件&#xff08;xml&#xff09;没有关联起来而导致的 出现这个问题请检查以下三处&#xff1a; 1、mapper接口所在包&#xff08;package&#xff09;的目录和xml映射文件所在的目录&#xff08;directory&#xff09;是否相同 特别要…

python的可迭代对象

一、介绍 在Python中&#xff0c;可迭代对象是指实现了__iter__()方法的对象&#xff0c;它可以通过调用iter()函数返回一个迭代器对象。可迭代对象可以是容器对象&#xff08;如列表、元组、集合、字典等&#xff09;&#xff0c;也可以是自定义的对象。 二、实例 以下是一…

Security+备考我想分想这几点

考试初衷 本人是一名信息安全从业者&#xff0c;听过很多信息安全方面的认证&#xff0c;如CISP、CISSP、CISA&#xff0c;但是没听过Security认证&#xff0c;偶然的机会&#xff0c;我的同事给我介绍了谷安&#xff0c;从这里我才了解到还有Security认证这么一个信息安全认证…

【嵌入式Linux项目】基于Linux的全志H616开发板智能家居项目(语音控制、人脸识别)

目录 一、功能需求 二、开发环境 1、硬件&#xff1a; 2、软件&#xff1a; 3、引脚分配&#xff1a; 三、关键点 1、设计模式之工厂模式 2、wiringPi库下的相关硬件操作函数调用 3、语音模块的串口通信 4、线程 5、摄像头的实时监控和拍照功能 6、人脸识别 四、编…

4个特别使用做图标设计的软件,赶紧收藏

图标设计软件是设计师日常工作中经常使用到的设计软件之一&#xff0c;本文精选了4个好用的图标设计软件为大家推荐&#xff0c;一起来看看吧&#xff01; 1、即时设计 即时设计是一款免费的&#xff0c;支持成员在线协作的图标设计软件&#xff0c;它提供了强大的设计功能和…

Kafka 入门到起飞 - Kafka怎么做到保障消息不会重复消费的? 消费者组是什么?

Kafka怎么做到避免消息重复消费的&#xff1f; 消费者组是什么&#xff1f; 消费者&#xff1a; 1、订阅Topic&#xff08;主题&#xff09; 2、从订阅的Topic消费&#xff08;pull&#xff09;消息&#xff0c; 3、将消费消息的offset&#xff08;偏移量&#xff09;保存在K…