返回顶部
首页 > 资讯 > 后端开发 > Python >Java多线程之Semaphore实现信号灯
  • 258
分享到

Java多线程之Semaphore实现信号灯

2024-04-02 19:04:59 258人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录1 Semaphore的主要方法2 实例讲解实现单例模式3 源码解析构造方法获取许可释放许可减小许可数量获取剩余许可数量前言: Semaphore是计数信号量。Semaphor

前言:

Semaphore是计数信号量。Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量。
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

1 Semaphore的主要方法

Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。

Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。

void acquire():当前线程尝试去阻塞的获取1个许可证。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了1个可用的许可证,则会停止等待,继续执行。
  • 当前线程被中断,则会抛出InterruptedException异常,并停止等待,继续执行。

void acquire(int n):从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。
当前线程尝试去阻塞的获取多个许可证。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了n个可用的许可证,则会停止等待,继续执行。
  • 当前线程被中断,则会抛出InterruptedException异常,并停止等待,继续执行。

void release():释放一个许可,将其返回给信号量。

void release(int n):释放n个许可。

int availablePermits():当前可用的许可数。
void acquierUninterruptibly():当前线程尝试去阻塞的获取1个许可证(不可中断的)。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了1个可用的许可证,则会停止等待,继续执行。

void acquireUninterruptibly(permits):当前线程尝试去阻塞的获取多个许可证。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了n个可用的许可证,则会停止等待,继续执行。

boolean tryAcquire()当前线程尝试去获取1个许可证。

  • 此过程是非阻塞的,它只是在方法调用时进行一次尝试。
  • 如果当前线程获取了1个可用的许可证,则会停止等待,继续执行,并返回true
  • 如果当前线程没有获得这个许可证,也会停止等待,继续执行,并返回false

boolean tryAcquire(permits):当前线程尝试去获取多个许可证。

  • 此过程是非阻塞的,它只是在方法调用时进行一次尝试。
  • 如果当前线程获取了permits个可用的许可证,则会停止等待,继续执行,并返回true
  • 如果当前线程没有获得permits个许可证,也会停止等待,继续执行,并返回false

boolean tryAcquire(timeout,TimeUnit):当前线程在限定时间内,阻塞的尝试去获取1个许可证。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了可用的许可证,则会停止等待,继续执行,并返回true
  • 当前线程等待时间timeout超时,则会停止等待,继续执行,并返回false
  • 当前线程在timeout时间内被中断,则会抛出InterruptedException一次,并停止等待,继续执行。

boolean tryAcquire(permits,timeout,TimeUnit):当前线程在限定时间内,阻塞的尝试去获取permits个许可证。

此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:

  • 当前线程获取了可用的permits个许可证,则会停止等待,继续执行,并返回true
  • 当前线程等待时间timeout超时,则会停止等待,继续执行,并返回false
  • 当前线程在timeout时间内被中断,则会抛出InterruptedException一次,并停止等待,继续执行。

2 实例讲解


public class SemaphoreTest {

    private static final Semaphore semaphore = new Semaphore(3);

    public static void main(String[] args) {
        Executor executor = Executors.newCachedThreadPool();

        String[] name = {"Jack", "Pony", "Larry", "Martin", "James", "ZhangSan","Tree"};

        int[] age = {21,22,23,24,25,26,27};

        for(int i=0;i<7;i++)
        {
            Thread t1=new InfORMationThread(name[i],age[i]);
            executor.execute(t1);
        }
    }

    private static class InformationThread extends Thread {
        private final String name;
        private final int age;

        public InformationThread(String name, int age) {
            this.name = name;
            this.age = age;
        }


        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()
                        + ":大家好,我是" + name + "我今年" + age +
                        "当前时间段为:" + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println(name + "要准备释放许可证了,当前时间为:" + System.currentTimeMillis());
                System.out.println("当前可使用的许可数为:" + semaphore.availablePermits());
                System.out.println("是否有正在等待许可证的线程:" + semaphore.hasQueuedThreads());
                System.out.println("正在等待许可证的队列长度(线程数量):" + semaphore.getQueueLength());
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

pool-1-thread-1:大家好,我是Jack我今年21当前时间段为:1543498535306
pool-1-thread-3:大家好,我是Larry我今年23当前时间段为:1543498535306
pool-1-thread-2:大家好,我是Pony我今年22当前时间段为:1543498535306
Pony要准备释放许可证了,当前时间为:1543498536310
Jack要准备释放许可证了,当前时间为:1543498536310
当前可使用的许可数为:0
Larry要准备释放许可证了,当前时间为:1543498536310
是否有正在等待许可证的线程:true
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):4
正在等待许可证的队列长度(线程数量):4
当前可使用的许可数为:0
pool-1-thread-4:大家好,我是Martin我今年24当前时间段为:1543498536311
是否有正在等待许可证的线程:true
pool-1-thread-5:大家好,我是James我今年25当前时间段为:1543498536311
正在等待许可证的队列长度(线程数量):2
pool-1-thread-6:大家好,我是ZhangSan我今年26当前时间段为:1543498536312
James要准备释放许可证了,当前时间为:1543498537315
Martin要准备释放许可证了,当前时间为:1543498537315
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):1
当前可使用的许可数为:0
是否有正在等待许可证的线程:false
pool-1-thread-7:大家好,我是Tree我今年27当前时间段为:1543498537316
正在等待许可证的队列长度(线程数量):0
ZhangSan要准备释放许可证了,当前时间为:1543498537317
当前可使用的许可数为:1
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0
Tree要准备释放许可证了,当前时间为:1543498538319
当前可使用的许可数为:2
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0

以上是非公平信号量,将建立Semaphore对象的语句改为如下语句:


private static final Semaphore semaphore=new Semaphore(3,true);

pool-1-thread-1:大家好,我是Jack我今年21当前时间段为:1543498810563
pool-1-thread-3:大家好,我是Larry我今年23当前时间段为:1543498810564
pool-1-thread-2:大家好,我是Pony我今年22当前时间段为:1543498810563
Jack要准备释放许可证了,当前时间为:1543498811564
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):4
pool-1-thread-4:大家好,我是Martin我今年24当前时间段为:1543498811564
Larry要准备释放许可证了,当前时间为:1543498811568
当前可使用的许可数为:0
Pony要准备释放许可证了,当前时间为:1543498811568
是否有正在等待许可证的线程:true
当前可使用的许可数为:0
正在等待许可证的队列长度(线程数量):3
是否有正在等待许可证的线程:true
pool-1-thread-5:大家好,我是James我今年25当前时间段为:1543498811568
正在等待许可证的队列长度(线程数量):2
pool-1-thread-6:大家好,我是ZhangSan我今年26当前时间段为:1543498811568
Martin要准备释放许可证了,当前时间为:1543498812566
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):1
pool-1-thread-7:大家好,我是Tree我今年27当前时间段为:1543498812566
James要准备释放许可证了,当前时间为:1543498812572
当前可使用的许可数为:0
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0
ZhangSan要准备释放许可证了,当前时间为:1543498812572
当前可使用的许可数为:1
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0
Tree要准备释放许可证了,当前时间为:1543498813568
当前可使用的许可数为:2
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0

实现单例模式

将创建信号量对象语句修改如下:


private static final Semaphore semaphore=new Semaphore(1);

运行程序,结果如下:

pool-1-thread-1:大家好,我是Jack我今年21当前时间段为:1543499053898
Jack要准备释放许可证了,当前时间为:1543499054903
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):6
pool-1-thread-2:大家好,我是Pony我今年22当前时间段为:1543499054904
Pony要准备释放许可证了,当前时间为:1543499055907
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):5
pool-1-thread-3:大家好,我是Larry我今年23当前时间段为:1543499055907
Larry要准备释放许可证了,当前时间为:1543499056909
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):4
pool-1-thread-4:大家好,我是Martin我今年24当前时间段为:1543499056909
Martin要准备释放许可证了,当前时间为:1543499057913
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):3
pool-1-thread-5:大家好,我是James我今年25当前时间段为:1543499057913
James要准备释放许可证了,当前时间为:1543499058914
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):2
pool-1-thread-6:大家好,我是ZhangSan我今年26当前时间段为:1543499058915
ZhangSan要准备释放许可证了,当前时间为:1543499059919
当前可使用的许可数为:0
是否有正在等待许可证的线程:true
正在等待许可证的队列长度(线程数量):1
pool-1-thread-7:大家好,我是Tree我今年27当前时间段为:1543499059919
Tree要准备释放许可证了,当前时间为:1543499060923
当前可使用的许可数为:0
是否有正在等待许可证的线程:false
正在等待许可证的队列长度(线程数量):0

如上可知,如果将给定许可数设置为1,就如同一个单例模式,即单个停车位,只有一辆车进,然后这辆车出来后,下一辆车才能进。

3 源码解析

Semaphore有两种模式,公平模式和非公平模式。公平模式就是调用acquire的顺序就是获取许可证的顺序,遵循FIFO;而非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证,而前面还有等待的线程。

构造方法

Semaphore有两个构造方法,如下:

   


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

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

获取许可

先从获取一个许可看起,并且先看非公平模式下的实现。首先看acquire方法,acquire方法有几个重载,但主要是下面这个方法


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

从上面可以看到,调用了SyncacquireSharedInterruptibly方法,该方法在父类AQS中,如下:


    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted()) //如果线程被中断了,抛出异常
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0) //获取许可失败,将线程加入到等待队列中
            doAcquireSharedInterruptibly(arg);
    }

AQS子类如果要使用共享模式的话,需要实现tryAcquireShared方法,下面看NonfairSync的该方法实现:


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

该方法调用了父类中的nonfairTyAcquireShared方法,如下:


final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //获取剩余许可数量
                int available = getState();
                //计算给完这次许可数量后的个数
                int remaining = available - acquires;
                //如果许可不够或者可以将许可数量重置的话,返回
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

从上面可以看到,只有在许可不够时返回值才会小于0,其余返回的都是剩余许可数量,这也就解释了,一旦许可不够,后面的线程将会阻塞。看完了非公平的获取,再看下公平的获取,

代码如下:


protected int tryAcquireShared(int acquires) {
            for (;;) {
                //如果前面有线程再等待,直接返回-1
                if (hasQueuedPredecessors())
                    return -1;
                //后面与非公平一样
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

从上面可以看到,FairSyncNonFairSync的区别就在于会首先判断当前队列中有没有线程在等待,如果有,就老老实实进入到等待队列;而不像NonfairSync一样首先试一把,说不定就恰好获得了一个许可,这样就可以插队了。
看完了获取许可后,再看一下释放许可。

释放许可

释放许可也有几个重载方法,但都会调用下面这个带参数的方法,


public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

releaseShared方法在AQS中,如下:


public final boolean releaseShared(int arg) {
        //如果改变许可数量成功
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

AQS子类实现共享模式的类需要实现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;
            }
        }

从上面可以看到,一旦CAS改变许可数量成功,那么就会调用doReleaseShared()方法释放阻塞的线程。

减小许可数量

Semaphore还有减小许可数量的方法,该方法可以用于用于当资源用完不能再用时,这时就可以减小许可证。代码如下:


protected void reducePermits(int reduction) {
        if (reduction < 0) throw new IllegalArgumentException();
        sync.reducePermits(reduction);
    }

可以看到,委托给了Sync,Sync的reducePermits方法如下:


  final void reducePermits(int reductions) {
            for (;;) {
                //得到当前剩余许可数量
                int current = getState();
                //得到减完之后的许可数量
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                //如果CAS改变成功
                if (compareAndSetState(current, next))
                    return;
            }
        }

从上面可以看到,就是CAS改变AQS中的state变量,因为该变量代表许可证的数量。

获取剩余许可数量

Semaphore还可以一次将剩余的许可数量全部取走,该方法是drain方法,

如下:


public int drainPermits() {
        return sync.drainPermits();
    }

Sync的实现如下:


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

可以看到,就是CAS将许可数量置为0。

4、总结

Semaphore是信号量,用于管理一组资源。其内部是基于AQS的共享模式,AQS的状态表示许可证的数量,在许可证数量不够时,线程将会被挂起;而一旦有一个线程释放一个资源,那么就有可能重新唤醒等待队列中的线程继续执行。

到此这篇关于Java多线程Semaphore实现信号灯的文章就介绍到这了,更多相关Java多线程 Semaphore实现信号灯内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java多线程之Semaphore实现信号灯

本文链接: https://lsjlt.com/news/155709.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java多线程之Semaphore实现信号灯
    目录1 Semaphore的主要方法2 实例讲解实现单例模式3 源码解析构造方法获取许可释放许可减小许可数量获取剩余许可数量前言: Semaphore是计数信号量。Semaphor...
    99+
    2024-04-02
  • Java多线程如何使用Semaphore实现信号灯
    这篇文章主要讲解了“Java多线程如何使用Semaphore实现信号灯”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程如何使用Semaphore实现信号灯”吧!前言:Semaph...
    99+
    2023-06-25
  • 分析Java并发编程之信号量Semaphore
    目录一、认识Semaphore1.1、Semaphore 的使用场景1.2、Semaphore 使用1.3、Semaphore 信号量的模型二、Semaphore 深入理解2.1、S...
    99+
    2024-04-02
  • Java并发编程之Semaphore(信号量)详解及实例
    Java并发编程之Semaphore(信号量)详解及实例概述通常情况下,可能有多个线程同时访问数目很少的资源,如客户端建立了若干个线程同时访问同一数据库,这势必会造成服务端资源被耗尽的地步,那么怎样能够有效的来控制不可预知的接入量呢?及在同...
    99+
    2023-05-31
    java 发编程 semaphore
  • 怎么看待Linux 多线程中的信号量Semaphore
    今天就跟大家聊聊有关怎么看待Linux 多线程中的信号量Semaphore,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。理解 Semaphore,从一个好的翻译开始Semaphore...
    99+
    2023-06-15
  • Java多线程协作作业之信号同步怎么实现
    本篇内容介绍了“Java多线程协作作业之信号同步怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、信号同步多线程很多时候是协作作业。...
    99+
    2023-06-30
  • java多线程之并发工具类CountDownLatch,CyclicBarrier和Semaphore
    目录CountDownLatchSemaphoreCyclicBarrier总结 CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作。 ...
    99+
    2024-04-02
  • java多线程实现交通灯管理系统
    本文实例为大家分享了java多线程实现交通灯管理系统的具体代码,供大家参考,具体内容如下 一. 项目要求 模拟实现十字路口的交通灯管理系统逻辑。详细需求例如以下: 1、异步随机生...
    99+
    2024-04-02
  • Java详解多线程协作作业之信号同步
    目录一、信号同步二、基于时间维度1、CountDownLatch2、CyclicBarrier三、基于信号维度一、信号同步 多线程很多时候是协作作业。比如4个线程对电商数据分季度统计...
    99+
    2024-04-02
  • Java中的多线程如何实现线程通信
    这篇文章将为大家详细讲解有关Java中的多线程如何实现线程通信,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java多线程中线程间的通信一、使用while方式来实现线程之间的通信packag...
    99+
    2023-05-31
    java 多线程 线程通信
  • Java多线程Semaphore工具的使用详解
    目录Semaphore 是一种用于控制线程并发访问数的同步工具。它通过维护一定数量的许可证来限制对共享资源的访问。 许可证的数量就是可以同时访问共享资源的线程数目。 当一个线程需要访...
    99+
    2023-05-19
    Java 多线程 Java Semaphore
  • Java编程之多线程死锁与线程间通信简单实现代码
    死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源;我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行...
    99+
    2023-05-30
    java 多线程 ava
  • Java中怎么实现多线程通信
    Java中怎么实现多线程通信,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。概述多线程通信问题,也就是生产者与消费者问题生产者和消费者为两个线程,两个线程在运行过程中交替睡眠,生...
    99+
    2023-06-20
  • Java中怎么实现线程间通信与信号量
    Java中怎么实现线程间通信与信号量,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1.信号量Semaphore先说说Semaphore,Semaphore可以控制某个资源可...
    99+
    2023-05-30
    java
  • linux多线程编程详解教程(线程通过信号量实现通信代码)
    线程分类 线程按照其调度者可以分为用户级线程和核心级线程两种。 (1)用户级线程 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持。在这里,...
    99+
    2022-06-04
    信号量 线程 多线程
  • Java多线程之定时器Timer的实现
    目录标准库中的Timer模拟实现Timer标准库中的Timer 标准库中有一个Timer类,java.util.Timer,核心方法为schedule,schedule有两个参数,第...
    99+
    2022-11-13
    Java 多线程 定时器Timer Java 定时器Timer Java 定时器
  • java多线程编程之管道通信详解
    上一章节讲了wait/notify通信,这一节我们来探讨使用管道进行通信。 java中提供了IO流使我们很方便的对数据进行操作,pipeStream是一种特殊的流,用于不同线程间直接传送数据。一个线程将数据发送到输出管道,另一个线程从输入管...
    99+
    2023-05-30
    java 多线程 管道通信
  • Java多线程之线程同步
    volatile 先看个例子 class Test { // 定义一个全局变量 private boolean isRun = true; // 从主线程调...
    99+
    2024-04-02
  • Java多线程编程实现socket通信示例代码
    流传于网络上有关Java多线程通信的编程实例有很多,这一篇还算比较不错,代码可用。下面看看具体内容。TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的...
    99+
    2023-05-30
    java socket通信 ava
  • 【JavaSE专栏80】多线程通信,多个线程之间如何实现信息传递和同步?
    作者主页:Designer 小郑 作者简介:3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN学院、蓝桥云课认证讲师。 主打方向:Vue、SpringB...
    99+
    2023-09-06
    java 开发语言 后端 多线程 jvm 多线程通信 线程 原力计划
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作