JAVA多线程(十四)Java多线程之Semaphore信号量

1.JAVA多线程(十四)Java多线程之Semaphore信号量

   Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。把它比作是控制流量的红绿灯,比如一条马路要限制流量,只允许同时有一百辆车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入马路,但是如果前一百辆中有五辆车已经离开了马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程在执行,离开马路就表示线程执行完成,看见红灯就表示线程被阻塞,不能执行。

1.1 Semaphore(信号量)-允许多个线程同时访问

   执行 acquire 方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个 release 方法增加一个许可证,这可能会释放一个阻塞的 acquire 方法。然而,其实并没有实际的许可证这个对象,Semaphore 只是维持了一个可获得许可证的数量。 Semaphore 经常用于限制获取某种资源的线程数量。

   以下代码模拟了对某个服务的并发请求,每次只能有 3 个客户端同时访问,请求总数为 10,代码如下:

package com.yuanxw.chapter14;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class SemaphoreExample {
    /** 随机数:使用long参数的所有64位作为因子值。 **/
    private static Random random = new Random(System.currentTimeMillis());
    /** 总线程数 **/
    private static final int TOTAL_THREAD_NUMBER = 10;
    /** 允许执行的线程数 **/
    final static int ACQUIRE_THREAD_NUMBER = 3;

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(ACQUIRE_THREAD_NUMBER);
        ExecutorService executorService = Executors.newFixedThreadPool(TOTAL_THREAD_NUMBER);
        IntStream.range(0,TOTAL_THREAD_NUMBER).forEach(i -> {
            executorService.execute(()->{
                try {
                    System.out.println(String.format("线程【%s】>>>>准备工作", Thread.currentThread().getName()));
                    semaphore.acquire();
                    System.out.println(String.format("线程【%s】工作===>开始", Thread.currentThread().getName()));
                    TimeUnit.SECONDS.sleep(random.nextInt(5));
                    System.out.println(String.format("线程【%s】工作<====结束", Thread.currentThread().getName()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            });
        });
        executorService.shutdown();

        // main线程监控信号量中许可数变化
        while (true) {
            if(executorService.isTerminated()){break;}
            System.out.println("信号量中当前可用的许可数:" + semaphore.availablePermits());
            System.out.println("等待获取的线程数:" + semaphore.getQueueLength());
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

执行结果:

信号量中当前可用的许可数:3
等待获取的线程数:0
线程【pool-1-thread-6】>>>>准备工作
线程【pool-1-thread-5】>>>>准备工作
线程【pool-1-thread-7】>>>>准备工作
线程【pool-1-thread-1】>>>>准备工作
线程【pool-1-thread-3】>>>>准备工作
线程【pool-1-thread-8】>>>>准备工作
线程【pool-1-thread-7】工作===>开始
线程【pool-1-thread-5】工作===>开始
线程【pool-1-thread-6】工作===>开始
线程【pool-1-thread-2】>>>>准备工作
线程【pool-1-thread-4】>>>>准备工作
线程【pool-1-thread-9】>>>>准备工作
线程【pool-1-thread-10】>>>>准备工作
信号量中当前可用的许可数:0
等待获取的线程数:7
信号量中当前可用的许可数:0
等待获取的线程数:7
线程【pool-1-thread-5】工作<====结束
线程【pool-1-thread-6】工作<====结束
线程【pool-1-thread-7】工作<====结束
线程【pool-1-thread-3】工作===>开始
线程【pool-1-thread-1】工作===>开始
线程【pool-1-thread-8】工作===>开始
信号量中当前可用的许可数:0
等待获取的线程数:4
信号量中当前可用的许可数:0
等待获取的线程数:4
线程【pool-1-thread-8】工作<====结束
线程【pool-1-thread-1】工作<====结束
线程【pool-1-thread-3】工作<====结束
线程【pool-1-thread-2】工作===>开始
线程【pool-1-thread-4】工作===>开始
线程【pool-1-thread-9】工作===>开始
信号量中当前可用的许可数:0
等待获取的线程数:1
信号量中当前可用的许可数:0
等待获取的线程数:1
线程【pool-1-thread-4】工作<====结束
线程【pool-1-thread-2】工作<====结束
线程【pool-1-thread-9】工作<====结束
线程【pool-1-thread-10】工作===>开始
信号量中当前可用的许可数:2
等待获取的线程数:0
信号量中当前可用的许可数:2
等待获取的线程数:0
线程【pool-1-thread-10】工作<====结束

1.3 Semaphore(信号量)-构造Lock锁

Semaphore类其实就是synchronized关键字的升级版,这个类主要作用就是控制线程并发的数量。当我们把许可的数量设置为1时,就变成了同步锁.

package com.yuanxw.chapter14;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * 信号量锁
 */
public class SemaphoreExampleV2 {
    /**
     * 随机数:使用long参数的所有64位作为因子值。
     **/
    private static Random random = new Random(System.currentTimeMillis());

    public static void main(String[] args) {
        final SemaphoreLock semaphoreLock = new SemaphoreLock();
        IntStream.range(0, 5).forEach(i -> {
            new Thread(() -> {
                try {
                    System.out.println(String.format("线程【%s】已经开始工作", Thread.currentThread().getName()));
                    semaphoreLock.lock();
                    System.out.println(String.format("线程【%s】获得锁", Thread.currentThread().getName()));
                    TimeUnit.SECONDS.sleep(random.nextInt(5));
                    System.out.println(String.format("线程【%s】已经释放锁", Thread.currentThread().getName()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphoreLock.unLock();
                }
            }).start();
        });
    }

    static class SemaphoreLock {
        /**
         * 在JDK1.8,需要加上volatile关键字
         * 构造一个 Semaphore与给只有一个数量的许可证
         **/
        private volatile static  Semaphore semaphore = new Semaphore(1);

        /**
         * 加锁方法
         *
         * @throws InterruptedException
         */
        public void lock() throws InterruptedException {
            semaphore.acquire(1);
        }
        /**
         * 解锁方法
         */
        public void unLock() {
            semaphore.release(1);
        }
    }
}

执行结果:

线程【Thread-1】已经开始工作
线程【Thread-3】已经开始工作
线程【Thread-0】已经开始工作
线程【Thread-4】已经开始工作
线程【Thread-2】已经开始工作
线程【Thread-1】获得锁
线程【Thread-1】已经释放锁
线程【Thread-0】获得锁
线程【Thread-0】已经释放锁
线程【Thread-3】获得锁
线程【Thread-3】已经释放锁
线程【Thread-4】获得锁
线程【Thread-4】已经释放锁
线程【Thread-2】获得锁
线程【Thread-2】已经释放锁

1.3 Semaphore(信号量)JDK源码-构造方法

Semaphore这两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。

  • 公平模式: 调用 acquire 的顺序就是获取许可证的顺序,遵循 FIFO;
  • 抢占式: 有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证,而前面还有等待的线程。
    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and nonfair fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     */
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and the given fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     * @param fair {@code true} if this semaphore will guarantee
     *        first-in first-out granting of permits under contention,
     *        else {@code false}
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

    – 以上为《JAVA多线程(十四)Java多线程之Semaphore信号量》,如有不当之处请指出,我后续逐步完善更正,大家共同提高。谢谢大家对我的关注。

——厚积薄发(yuanxw)

发布了105 篇原创文章 · 获赞 162 · 访问量 46万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览