Semaphore-信号灯的简单实用

当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目,它是一个计数信号量,从概念上讲,信号量维护了一个许可集合,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可,每个release() 添加一个许可,从而可能释放一个正在阻塞的获取者。        

在线程池内创建线程并运行时,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,线程返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用acquire() 时无法保持同步锁定,因为这会阻止线程返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。下面通过一个例子加以说明:

@Slf4j
public class SemaphoreExample1 {
    private final static int threadCount = 10;
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= threadCount; i++) {
            final int threadNum = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire(); // 获取一个许可
                    test();
                    semaphore.release(); // 释放一个许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
    private static void test() throws InterruptedException {
        log.info("" + Thread.currentThread().getName());
        Thread.sleep(1000);
    }
}

 该例子定义了一个newCachedThreadPool,在该Pool中利用for循环同时创建10个线程,现在通过Semaphore,创建一个只允许在线程池中有3个线程并发运行,semaphore.acquire()表示某个线程获得了一个信号灯,开始运行,在运行结束时,通过semaphore.release()还回这个信号灯,以便剩下的线程获得信号灯运行,上例运行结果如下:

12:23:00.559 [pool-1-thread-1] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-1
12:23:00.559 [pool-1-thread-3] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-3
12:23:00.559 [pool-1-thread-2] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-2
12:23:01.562 [pool-1-thread-5] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-5
12:23:01.562 [pool-1-thread-4] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-4
12:23:01.562 [pool-1-thread-6] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-6
12:23:02.563 [pool-1-thread-9] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-9
12:23:02.563 [pool-1-thread-7] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-7
12:23:02.563 [pool-1-thread-8] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-8
12:23:03.563 [pool-1-thread-10] INFO com.lzc.concurrency.example.aqs.SemaphoreExample1 - pool-1-thread-10

由运行结果可知,每次最多只有三个线程同时运行。

上面的例子是如果未获得许可则阻塞自己,直到获取到许可然后继续执行。下面介绍另外一种用法:等待一定时间后还获取不到许可则丢弃该线程。

@Slf4j
public class SemaphoreExample3 {
    private final static int threadCount = 10;
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= threadCount; i++) {
            final int threadNum = i;
            executorService.execute(() -> {
                try {
                    //  无参:尝试获取一个许可,获取不到的直接丢弃
                    // 有参: 尝试获取一个许可,获取不到则等待一定时间,超过时间还获取不到则丢弃
                    if(semaphore.tryAcquire(1,TimeUnit.SECONDS)) {
                        test();
                        semaphore.release(); // 释放一个许可
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

    private static void test() throws InterruptedException {
        log.info(Thread.currentThread().getName());
        Thread.sleep(500);
    }
}

运行结果如下

12:39:02.174 [pool-1-thread-3] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-3
12:39:02.175 [pool-1-thread-2] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-2
12:39:02.175 [pool-1-thread-1] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-1
12:39:02.677 [pool-1-thread-5] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-5
12:39:02.677 [pool-1-thread-4] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-4
12:39:02.677 [pool-1-thread-6] INFO com.lzc.concurrency.example.aqs.SemaphoreExample3 - pool-1-thread-6

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/lizc_lizc/article/details/81143388