Semaphore,动态增减信号量

【信号量】:

  • 用于控制对某资源访问的同一时间的并发量。

 

【如何获取】:

  • semaphore.tryAcquire(),尝试获取,不阻塞
  • semaphore.acquire(),没信号量可用时,将进行阻塞等

 

【如何释放】:

  • semaphore.release();
  • 线程抛出各种异常,都别忘了在finally中释放信号量;
  • 如果释放的比获取的信号量还多,例如获取了2个,释放了5次,那么当前信号量就动态的增加为5了,要注意。

 

【动态增加】:

  • 多释放几次,就可以达到信号量动态增加的效果了

 

【动态减小】:

  • 信号量本来有这个api的,不过是protected方法,所以我们需要显式继续Semaphore,并重新实现该api,见ResizeableSemaphore类中的reducePermits(int reduction);
  • 举例如下:(该表格有个假设前提,不存在多余的release而产生出新的信号量,即release次数<=acquire次数)
当前信号量A
(A=A1+A2)
占用信号量A1可用信号量A2重新设置信号量B
(B=B1+B2)
当前可用的信号量B1当前待释放的量B2
532300
53210-2
532960



 

package com.jd.las.basic.service;

import java.util.concurrent.Semaphore;

/**
 * 
 * Title: 动态信号量<br>
 * 
 * Description: <br>
 * 
 * Company: <a href=www.jd.com>京东</a><br>
 * 
 * @author <a href=mailto:longzhun@jd.com>龙准</a>
 * 
 * @date 2015年5月26日 下午7:29:42
 */
public class JdSemaphore {

	 /**
     * semaphore starts at 0 capacity; must be set by setMaxPermits before use
     */
    private final ResizeableSemaphore semaphore = new ResizeableSemaphore();
 
    /**
     * how many permits are allowed as governed by this semaphore.
     * Access must be synchronized on this object.
     */
    private int maxPermits = 0;
 
    /**
     * New instances should be configured with setMaxPermits().
     */
    public JdSemaphore() {
        // no op
    }
 
    /*
     * Must be synchronized because the underlying int is not thread safe
     */
    /**
     * Set the max number of permits. Must be greater than zero.
     *
     * Note that if there are more than the new max number of permits currently
     * outstanding, any currently blocking threads or any new threads that start
     * to block after the call will wait until enough permits have been released to
     * have the number of outstanding permits fall below the new maximum. In
     * other words, it does what you probably think it should.
     *
     * @param newMax
     */
    public synchronized void setMaxPermits(int newMax) {
        if (newMax < 1) {
            throw new IllegalArgumentException("Semaphore size must be at least 1,"
                + " was " + newMax);
        }
 
        int delta = newMax - this.maxPermits;
 
        if (delta == 0) {
            return;
        } else if (delta > 0) {
            // new max is higher, so release that many permits
            this.semaphore.release(delta);
        } else {
            delta *= -1;
            // delta < 0.
            // reducePermits needs a positive #, though.
            this.semaphore.reducePermits(delta);
        }
 
        this.maxPermits = newMax;
    }
 
    /**
     * Release a permit back to the semaphore. Make sure not to double-release.
     *
     */
    public void release() {
        this.semaphore.release();
    }
 
    /**
     * Get a permit, blocking if necessary.
     *
     * @throws InterruptedException
     *             if interrupted while waiting for a permit
     */
    public void acquire() throws InterruptedException {
        this.semaphore.acquire();
    }
 
    /**
     * A trivial subclass of <code>Semaphore</code> that exposes the reducePermits
     * call to the parent class. Doug Lea says it's ok...
     * http://osdir.com/ml/java.jsr.166-concurrency/2003-10/msg00042.html
     */
    private static final class ResizeableSemaphore extends Semaphore {
        /**
         *
         */
        private static final long serialVersionUID = 1L;
 
        /**
         * Create a new semaphore with 0 permits.
         */
        ResizeableSemaphore() {
            super(0);
        }
 
        @Override
        protected void reducePermits(int reduction) {
            super.reducePermits(reduction);
        }
    }

}

 

这里可参考:http://blog.teamlazerbeez.com/2009/04/20/javas-semaphore-resizing/

 

### 进程间通信中的信号量使用与实现 #### 什么是信号量信号量是一种用于解决多线程或多进程同步问题的数据结构。它可以用来协调多个进程之间的资源访问,防止竞争条件的发生。信号量分为两种主要形式:二进制信号量和计数信号量[^1]。 #### 创建信号量集 通过 `semget` 函数可以创建一个新的信号量集合或者获取已有的信号量集合。该函数的第一个参数是一个键值(通常称为 IPC 键),这个键允许不相关的进程共享同一组信号量。如果指定了 `-1` 或者其他特殊标志,则会自动分配一个唯一的键[^3]。 ```c int semid; key_t key = ftok("/tmp", 'a'); // 使用ftok生成唯一key if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) { perror("Failed to create semaphore"); } ``` 上述代码片段展示了如何利用 `semget()` 来初始化一个信号量对象并获得它的 ID 值。 #### 操作信号量 一旦获得了信号量标识符之后,就可以调用 `semop` 对这些信号量执行各种操作,比如增加或减少它们的值。这通常是通过传递给 `semop` 的数组来完成的,其中每个条目描述了一次单独的操作。 ```c struct sembuf sop; sop.sem_num = 0; /* Signal number */ sop.sem_op = -1; /* Decrement value by one (P operation)*/ sop.sem_flg = SEM_UNDO; /* Undo on process exit */ if (semop(semid, &sop, 1) == -1){ perror("Semaphore P Operation failed."); } // 后续逻辑... sop.sem_op = +1; /* Increment value by one (V operation)*/ if (semop(semid, &sop, 1) == -1){ perror("Semaphore V Operation failed."); } ``` 这里演示了两个基本操作:“等待”(即减去单位数量直到成功),以及“发送”(即加上单位数量)。 #### 控制信号量属性 除了简单的增减之外,还可以调整整个信号量集合的状态信息,例如设置权限位图、删除队列等等。这是借助于另一个名为 `semctl` 的工具来进行管理工作的。 ```c union semun arg; arg.val = 1; /* Set initial value of first semaphore in set */ if (semctl(semid, 0, SETVAL, arg) == -1){ perror("Setting Initial Value Failed!"); } ``` 这段例子说明怎样把某个特定索引位置上的信号重新赋初值为一[^2]。 #### 处理异步事件的通知机制—信号(signal) 虽然严格意义上讲这不是传统意义上的IPC手段之一,但是当提到跨不同地址空间之间通讯的时候也不能忽略掉它的重要性。因为某些情况下可能希望知道什么时候发生了外部中断等情况下来采取行动而不是一直轮询浪费CPU时间片。因此引入了类似于这样的API接口: ```c #include <signal.h> void handler(int sig){ printf("Caught signal %d\n",sig); } if(signal(SIGINT,&handler)==SIG_ERR){ fprintf(stderr,"Can't catch SIGINT\n"); } while(1); /* Keep running until interrupted */ ``` 此段脚本设置了自定义处理器以便能够响应来自键盘输入产生的终止请求。 ### 总结 综上所述,在UNIX/Linux环境下进行有效的进程间交互离不开合理运用诸如消息队列、管道、套接字乃至本文重点讨论过的信号量等丰富的中间件设施支持下的编程技巧实践应用过程之中不断积累经验教训从而达到更加高效稳定可靠的软件开发目标上去努力奋斗前行之路永无止境!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值