Semaphore释疑

java api中Semaphore(信号量),用于控制有限资源的并发访问。API也非常好理解,不过有几个需要注意的地方:

  1. Semaphore是纯粹的应用级控制“锁”,使用简单的volitale变量作为信号量信息,通过acquire、release、reduce等显式的可以修改此信号量数字。
  2. 它并没有维护任何锁,也不是控制reentrant的,它不会维护信号和thread的关系。
  3. Semaphore的初始值可以为0,甚至可以为负数。对于acquire调用(信号down),它只会比较现在信号值与0的大小关系,如果<=0那么将不能获得授权。
  4. 对于release(信号up),只是简单的对信号值进行原子增加,经过多次的release,信号值可以超过初始的阀值。
  5. 对于Semaphore(0/-N)的场景,有特殊的使用,这种信号控制,在可以acquire之前,必须经过约定的足够多的release之后才可以被使用。

参考:http://stackoverflow.com/questions/1221322/how-does-semaphore-work

 

Calling down when it's 0 should not work. Calling up when it's 3 does work. (I am thinking of Java).

 

Let me add some more. Many people think of locks like (binary) semaphores (ie - N = 1, so the value of the semaphore is either 0 (held) or 1 (not held)). But this is not quite right. A lock has a notion of "ownership" so it may be "reentrant". That means that a thread that holds a lock, is allowed to call lock() again (effectively moving the count from 0 to -1), because the thread already holds the lock and is allowed to "reenter" it. Locks can also be non reentrant. A lock holder is expected to call unlock() the same number of times as lock().

 

Semaphores have no notion of ownership, so they cannot be reentrant, although as many permits as are available may be acquired. That means a thread needs to block when it encounters a value of 0, until someone increments the semaphore.

 

Also, in what I have seen (which is Java), you can increment the semaphore greater than N, and that also sort of has to do with ownership: a Semaphore has no notion of ownership so anybody can give it more permits. Unlike a thread, where whenever a thread calls unlock() without holding a lock, that is an error. (In java it will throw an exception).

 

### 使用 `Semaphore` 避免并发修改异常 在多线程环境中,当多个线程同时访问并修改共享资源(如集合)时,可能会引发 `ConcurrentModificationException`。为了防止这种异常,可以通过 `Semaphore` 来控制对共享资源的访问,确保同一时间只有有限数量的线程可以操作资源,从而避免并发冲突。 `Semaphore` 是一种计数信号量,它通过维护一组许可(permits)来控制线程的访问。线程在访问资源前必须先获取许可,访问结束后释放许可。如果许可数为零,后续请求许可的线程将被阻塞,直到有其他线程释放许可[^3]。 #### 控制线程访问共享集合 例如,在对一个共享列表进行操作时,可以通过 `Semaphore` 来限制同时操作该列表的线程数量,从而避免多个线程同时修改列表结构。以下是一个使用 `Semaphore` 控制并发修改的示例: ```java import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SafeListModification { private static final List<Integer> sharedList = new ArrayList<>(); private static final Semaphore semaphore = new Semaphore(2); // 允许最多两个线程同时修改列表 public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { final int index = i; executor.submit(() -> { try { semaphore.acquire(); sharedList.add(index); Thread.sleep(100); // 模拟耗时操作 System.out.println("Added: " + index + ", Current list: " + sharedList); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); } }); } executor.shutdown(); } } ``` 在这个示例中,`Semaphore` 初始化为允许两个线程同时访问共享列表。每个线程在添加元素前必须先获取许可,操作完成后释放许可。这种方式可以有效避免因多个线程同时修改列表而导致的并发修改异常[^1]。 #### 使用 `Semaphore` 控制阶段性任务的执行顺序 除了限制并发访问,`Semaphore` 还可以用于控制任务的执行顺序。例如,在某些场景下,需要确保一批线程在某个阶段完成后才能继续执行后续操作。以下是一个使用 `Semaphore` 控制阶段性任务执行的示例: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class PhasedExecutionControl { private static final Semaphore semaphore = new Semaphore(0); // 初始信号量为 0 public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { executor.submit(() -> { try { semaphore.acquire(); // 等待信号量释放 System.out.println(Thread.currentThread().getName() + " is executing."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } System.out.println("准备释放 5 个信号量"); semaphore.release(5); // 释放 5 个信号量,允许前 5 个线程执行 try { Thread.sleep(2000); // 模拟等待第一批任务完成 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("再次释放 5 个信号量"); semaphore.release(5); // 再次释放 5 个信号量,允许剩余线程执行 } } ``` 在这个示例中,`Semaphore` 被初始化为 0,所有线程启动后都会阻塞在 `acquire()` 方法上。主线程通过两次调用 `release()` 方法分阶段释放信号量,从而控制线程的执行顺序。这种机制可以用于协调多个阶段的任务执行流程,确保线程按预期顺序访问共享资源[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值